substreams_solana_core/
lib.rs1use std::ops::Deref;
2
3use address::Address;
4use pb::sf::solana::r#type::v1::{CompiledInstruction, InnerInstruction, Transaction};
5
6use crate::pb::sf::solana::r#type::v1::ConfirmedTransaction;
7
8pub mod address;
9pub mod pb;
10
11pub mod block_view;
13
14pub mod base58 {
16 pub fn encode<T: AsRef<[u8]>>(data: T) -> String {
23 bs58::encode(data.as_ref()).into_string()
24 }
25
26 pub fn decode<T: AsRef<str>>(data: T) -> Result<Vec<u8>, bs58::decode::Error> {
33 bs58::decode(data.as_ref()).into_vec()
34 }
35}
36
37pub trait Instruction {
41 fn program_id_index(&self) -> u32;
43
44 fn accounts(&self) -> &Vec<u8>;
47 fn data(&self) -> &Vec<u8>;
48 fn stack_height(&self) -> Option<u32>;
49}
50
51impl<'a> Instruction for Box<dyn Instruction + 'a> {
52 fn program_id_index(&self) -> u32 {
53 self.deref().program_id_index()
54 }
55
56 fn accounts(&self) -> &Vec<u8> {
57 self.deref().accounts()
58 }
59
60 fn data(&self) -> &Vec<u8> {
61 self.deref().data()
62 }
63
64 fn stack_height(&self) -> Option<u32> {
65 self.deref().stack_height()
66 }
67}
68
69impl<'a> Instruction for &Box<dyn Instruction + 'a> {
70 fn program_id_index(&self) -> u32 {
71 (*self).deref().program_id_index()
72 }
73
74 fn accounts(&self) -> &Vec<u8> {
75 (*self).deref().accounts()
76 }
77
78 fn data(&self) -> &Vec<u8> {
79 (*self).deref().data()
80 }
81
82 fn stack_height(&self) -> Option<u32> {
83 (*self).deref().stack_height()
84 }
85}
86
87impl Instruction for CompiledInstruction {
88 fn program_id_index(&self) -> u32 {
89 self.program_id_index
90 }
91
92 fn accounts(&self) -> &Vec<u8> {
93 &self.accounts
94 }
95
96 fn data(&self) -> &Vec<u8> {
97 &self.data
98 }
99
100 fn stack_height(&self) -> Option<u32> {
101 Some(0)
102 }
103}
104
105impl Instruction for &CompiledInstruction {
106 fn program_id_index(&self) -> u32 {
107 self.program_id_index
108 }
109
110 fn accounts(&self) -> &Vec<u8> {
111 &self.accounts
112 }
113
114 fn data(&self) -> &Vec<u8> {
115 &self.data
116 }
117
118 fn stack_height(&self) -> Option<u32> {
119 Some(0)
120 }
121}
122
123impl Instruction for InnerInstruction {
124 fn program_id_index(&self) -> u32 {
125 self.program_id_index
126 }
127
128 fn accounts(&self) -> &Vec<u8> {
129 &self.accounts
130 }
131
132 fn data(&self) -> &Vec<u8> {
133 &self.data
134 }
135
136 fn stack_height(&self) -> Option<u32> {
137 self.stack_height
138 }
139}
140
141impl Instruction for &InnerInstruction {
142 fn program_id_index(&self) -> u32 {
143 self.program_id_index
144 }
145
146 fn accounts(&self) -> &Vec<u8> {
147 &self.accounts
148 }
149
150 fn data(&self) -> &Vec<u8> {
151 &self.data
152 }
153
154 fn stack_height(&self) -> Option<u32> {
155 self.stack_height
156 }
157}
158
159impl ConfirmedTransaction {
160 pub fn id(&self) -> String {
165 self.transaction.as_ref().unwrap().id()
166 }
167
168 pub fn hash(&self) -> &[u8] {
173 self.transaction.as_ref().unwrap().hash()
174 }
175
176 pub fn resolved_accounts(&self) -> Vec<&Vec<u8>> {
189 let meta = self.meta.as_ref().unwrap();
190 let message = self.transaction.as_ref().unwrap().message.as_ref().unwrap();
191
192 let mut accounts = vec![];
193 accounts.extend(message.account_keys.iter());
194 accounts.extend(meta.loaded_writable_addresses.iter());
195 accounts.extend(meta.loaded_readonly_addresses.iter());
196
197 accounts
198 }
199
200 pub fn account_at<'a>(&'a self, index: u8) -> Address<'a> {
204 let mut i: usize = index as usize;
205
206 let account_keys = &self
207 .transaction
208 .as_ref()
209 .unwrap()
210 .message
211 .as_ref()
212 .unwrap()
213 .account_keys;
214
215 if i < account_keys.len() {
216 return Address(&account_keys[i]);
217 }
218
219 let meta = self.meta.as_ref().unwrap();
220
221 i = i - account_keys.len();
222 if i < meta.loaded_writable_addresses.len() {
223 return Address(&meta.loaded_writable_addresses[i]);
224 }
225
226 i = i - meta.loaded_writable_addresses.len();
227 if i < meta.loaded_readonly_addresses.len() {
228 return Address(&meta.loaded_readonly_addresses[i]);
229 }
230
231 panic!("Account index {} out of bounds", index);
232 }
233}
234
235impl Transaction {
236 pub fn id(&self) -> String {
239 bs58::encode(self.hash()).into_string()
240 }
241
242 pub fn hash(&self) -> &[u8] {
245 &self.signatures[0]
246 }
247}
248
249#[cfg(test)]
250mod tests {
251 use crate::pb::sf::solana::r#type::v1 as pb;
252 use pretty_assertions::assert_eq;
253
254 #[test]
255 fn it_resolves_account_correctly() {
256 let trx = pb::ConfirmedTransaction {
257 transaction: Some(pb::Transaction {
258 signatures: vec![vec![1, 2, 3]],
259 message: Some(pb::Message {
260 account_keys: vec![bytes("a0"), bytes("a1"), bytes("a2")],
261 ..Default::default()
262 }),
263 }),
264 meta: Some(pb::TransactionStatusMeta {
265 loaded_writable_addresses: vec![bytes("a3"), bytes("a4")],
266 loaded_readonly_addresses: vec![bytes("a5"), bytes("a6")],
267 ..Default::default()
268 }),
269 };
270
271 assert_eq!(
272 vec![
273 bytes("a0"),
274 bytes("a1"),
275 bytes("a2"),
276 bytes("a3"),
277 bytes("a4"),
278 bytes("a5"),
279 bytes("a6")
280 ],
281 trx.resolved_accounts()
282 .into_iter()
283 .cloned()
284 .collect::<Vec<_>>()
285 );
286
287 assert_eq!(bytes("a0"), trx.account_at(0));
288 assert_eq!(bytes("a1"), trx.account_at(1));
289 assert_eq!(bytes("a2"), trx.account_at(2));
290 assert_eq!(bytes("a3"), trx.account_at(3));
291 assert_eq!(bytes("a4"), trx.account_at(4));
292 assert_eq!(bytes("a5"), trx.account_at(5));
293 assert_eq!(bytes("a6"), trx.account_at(6));
294 }
295
296 fn bytes(s: &str) -> Vec<u8> {
297 ::hex::decode(s).unwrap()
298 }
299}