1use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6use crate::models::{MatchConditions, Monitor, SolanaBlock, SolanaTransaction};
7
8#[derive(Debug, Clone, Deserialize, Serialize)]
10pub struct MonitorMatch {
11 pub monitor: Monitor,
13
14 pub transaction: SolanaTransaction,
16
17 pub block: SolanaBlock,
19
20 pub network_slug: String,
22
23 pub matched_on: MatchConditions,
25
26 pub matched_on_args: Option<MatchArguments>,
28}
29
30#[derive(Debug, Clone, Deserialize, Serialize)]
32pub struct MatchParamsMap {
33 pub signature: String,
35
36 pub args: Option<Vec<MatchParamEntry>>,
38}
39
40#[derive(Debug, Clone, Deserialize, Serialize)]
42pub struct MatchParamEntry {
43 pub name: String,
45
46 pub value: String,
48
49 pub kind: String,
51
52 pub indexed: bool,
54}
55
56#[derive(Debug, Clone, Deserialize, Serialize)]
58pub struct MatchArguments {
59 pub functions: Option<Vec<MatchParamsMap>>,
61
62 pub events: Option<Vec<MatchParamsMap>>,
64}
65
66#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct ParsedInstructionResult {
69 pub program_id: String,
71
72 pub instruction_name: String,
74
75 pub instruction_signature: String,
77
78 pub arguments: Vec<Value>,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct DecodedParamEntry {
88 pub value: String,
90
91 pub kind: String,
93
94 pub indexed: bool,
96}
97
98#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)]
103pub struct ContractSpec {
104 pub version: String,
106
107 pub name: String,
109
110 pub instructions: Vec<IdlInstruction>,
112
113 #[serde(default)]
115 pub accounts: Vec<IdlAccount>,
116
117 #[serde(default)]
119 pub events: Vec<IdlEvent>,
120
121 #[serde(default)]
123 pub types: Vec<IdlTypeDef>,
124
125 #[serde(default)]
127 pub errors: Vec<IdlError>,
128
129 #[serde(default)]
131 pub metadata: Option<IdlMetadata>,
132}
133
134#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)]
136pub struct IdlInstruction {
137 pub name: String,
139
140 #[serde(default)]
142 pub discriminator: Option<Vec<u8>>,
143
144 #[serde(default)]
146 pub accounts: Vec<IdlAccountItem>,
147
148 #[serde(default)]
150 pub args: Vec<IdlField>,
151
152 #[serde(default)]
154 pub docs: Vec<String>,
155}
156
157#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
159#[serde(untagged)]
160pub enum IdlAccountItem {
161 Single(IdlInstructionAccount),
163 Composite(IdlInstructionAccounts),
165}
166
167impl Default for IdlAccountItem {
168 fn default() -> Self {
169 IdlAccountItem::Single(IdlInstructionAccount::default())
170 }
171}
172
173#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)]
175pub struct IdlInstructionAccount {
176 pub name: String,
178
179 #[serde(default)]
181 pub is_mut: bool,
182
183 #[serde(default)]
185 pub is_signer: bool,
186
187 #[serde(default)]
189 pub is_optional: bool,
190
191 #[serde(default)]
193 pub docs: Vec<String>,
194
195 #[serde(default)]
197 pub pda: Option<IdlPda>,
198}
199
200#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)]
202pub struct IdlInstructionAccounts {
203 pub name: String,
205
206 pub accounts: Vec<IdlAccountItem>,
208}
209
210#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)]
212pub struct IdlPda {
213 pub seeds: Vec<IdlSeed>,
215
216 #[serde(default)]
218 pub program_id: Option<String>,
219}
220
221#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
223#[serde(tag = "kind")]
224pub enum IdlSeed {
225 #[serde(rename = "const")]
227 Const { value: Value },
228 #[serde(rename = "account")]
230 Account { path: String },
231 #[serde(rename = "arg")]
233 Arg { path: String },
234}
235
236impl Default for IdlSeed {
237 fn default() -> Self {
238 IdlSeed::Const { value: Value::Null }
239 }
240}
241
242#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)]
244pub struct IdlField {
245 pub name: String,
247
248 #[serde(rename = "type")]
250 pub field_type: IdlType,
251
252 #[serde(default)]
254 pub docs: Vec<String>,
255}
256
257#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
259#[serde(untagged)]
260pub enum IdlType {
261 Primitive(String),
263 Complex(IdlTypeComplex),
265}
266
267impl Default for IdlType {
268 fn default() -> Self {
269 IdlType::Primitive("u8".to_string())
270 }
271}
272
273#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
275#[serde(rename_all = "camelCase")]
276pub enum IdlTypeComplex {
277 Array(Box<IdlType>, usize),
279 Vec(Box<IdlType>),
281 Option(Box<IdlType>),
283 Defined(String),
285 Tuple(Vec<IdlType>),
287}
288
289impl Default for IdlTypeComplex {
290 fn default() -> Self {
291 IdlTypeComplex::Defined("Unknown".to_string())
292 }
293}
294
295#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)]
297pub struct IdlAccount {
298 pub name: String,
300
301 #[serde(default)]
303 pub discriminator: Option<Vec<u8>>,
304
305 #[serde(default)]
307 pub docs: Vec<String>,
308}
309
310#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)]
312pub struct IdlEvent {
313 pub name: String,
315
316 #[serde(default)]
318 pub discriminator: Option<Vec<u8>>,
319
320 #[serde(default)]
322 pub fields: Vec<IdlEventField>,
323}
324
325#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)]
327pub struct IdlEventField {
328 pub name: String,
330
331 #[serde(rename = "type")]
333 pub field_type: IdlType,
334
335 #[serde(default)]
337 pub index: bool,
338}
339
340#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)]
342pub struct IdlTypeDef {
343 pub name: String,
345
346 #[serde(rename = "type")]
348 pub type_def: IdlTypeDefTy,
349
350 #[serde(default)]
352 pub docs: Vec<String>,
353}
354
355#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
357#[serde(tag = "kind")]
358pub enum IdlTypeDefTy {
359 #[serde(rename = "struct")]
361 Struct { fields: Vec<IdlField> },
362 #[serde(rename = "enum")]
364 Enum { variants: Vec<IdlEnumVariant> },
365}
366
367impl Default for IdlTypeDefTy {
368 fn default() -> Self {
369 IdlTypeDefTy::Struct { fields: vec![] }
370 }
371}
372
373#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)]
375pub struct IdlEnumVariant {
376 pub name: String,
378
379 #[serde(default)]
381 pub fields: Option<Vec<IdlField>>,
382}
383
384#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)]
386pub struct IdlError {
387 pub code: u32,
389
390 pub name: String,
392
393 #[serde(default)]
395 pub msg: String,
396}
397
398#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)]
400pub struct IdlMetadata {
401 #[serde(default)]
403 pub address: String,
404}
405
406impl ContractSpec {
407 pub fn get_instruction(&self, name: &str) -> Option<&IdlInstruction> {
409 self.instructions.iter().find(|i| i.name == name)
410 }
411
412 pub fn get_event(&self, name: &str) -> Option<&IdlEvent> {
414 self.events.iter().find(|e| e.name == name)
415 }
416
417 pub fn get_instruction_signature(&self, name: &str) -> Option<String> {
419 self.get_instruction(name).map(|i| {
420 let args: Vec<String> = i
421 .args
422 .iter()
423 .map(|a| idl_type_to_string(&a.field_type).to_string())
424 .collect();
425 format!("{}({})", i.name, args.join(","))
426 })
427 }
428
429 pub fn get_event_signature(&self, name: &str) -> Option<String> {
431 self.get_event(name).map(|e| {
432 let fields: Vec<String> = e
433 .fields
434 .iter()
435 .map(|f| idl_type_to_string(&f.field_type))
436 .collect();
437 format!("{}({})", e.name, fields.join(","))
438 })
439 }
440}
441
442fn idl_type_to_string(ty: &IdlType) -> String {
444 match ty {
445 IdlType::Primitive(s) => s.clone(),
446 IdlType::Complex(c) => match c {
447 IdlTypeComplex::Array(inner, size) => {
448 format!("[{};{}]", idl_type_to_string(inner), size)
449 }
450 IdlTypeComplex::Vec(inner) => format!("Vec<{}>", idl_type_to_string(inner)),
451 IdlTypeComplex::Option(inner) => format!("Option<{}>", idl_type_to_string(inner)),
452 IdlTypeComplex::Defined(name) => name.clone(),
453 IdlTypeComplex::Tuple(types) => {
454 let types_str: Vec<String> = types.iter().map(idl_type_to_string).collect();
455 format!("({})", types_str.join(","))
456 }
457 },
458 }
459}
460
461impl std::fmt::Display for ContractSpec {
463 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
464 match serde_json::to_string(self) {
465 Ok(s) => write!(f, "{}", s),
466 Err(e) => {
467 tracing::error!("Error serializing contract spec: {:?}", e);
468 write!(f, "")
469 }
470 }
471 }
472}
473
474#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)]
479pub struct FormattedContractSpec {
480 pub functions: Vec<ContractFunction>,
482
483 pub events: Vec<ContractEvent>,
485}
486
487impl From<ContractSpec> for FormattedContractSpec {
488 fn from(spec: ContractSpec) -> Self {
489 let functions = spec
490 .instructions
491 .iter()
492 .map(|i| {
493 let inputs: Vec<ContractInput> = i
494 .args
495 .iter()
496 .enumerate()
497 .map(|(idx, arg)| ContractInput {
498 index: idx as u32,
499 name: arg.name.clone(),
500 kind: idl_type_to_string(&arg.field_type),
501 })
502 .collect();
503
504 let signature = format!(
505 "{}({})",
506 i.name,
507 inputs
508 .iter()
509 .map(|i| i.kind.clone())
510 .collect::<Vec<_>>()
511 .join(",")
512 );
513
514 ContractFunction {
515 name: i.name.clone(),
516 inputs,
517 signature,
518 }
519 })
520 .collect();
521
522 let events = spec
523 .events
524 .iter()
525 .map(|e| {
526 let params: Vec<ContractEventParam> = e
527 .fields
528 .iter()
529 .map(|f| ContractEventParam {
530 name: f.name.clone(),
531 kind: idl_type_to_string(&f.field_type),
532 indexed: f.index,
533 })
534 .collect();
535
536 let signature = format!(
537 "{}({})",
538 e.name,
539 params
540 .iter()
541 .map(|p| p.kind.clone())
542 .collect::<Vec<_>>()
543 .join(",")
544 );
545
546 ContractEvent {
547 name: e.name.clone(),
548 params,
549 signature,
550 }
551 })
552 .collect();
553
554 FormattedContractSpec { functions, events }
555 }
556}
557
558#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)]
560pub struct ContractFunction {
561 pub name: String,
563
564 pub inputs: Vec<ContractInput>,
566
567 pub signature: String,
569}
570
571#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)]
573pub struct ContractInput {
574 pub index: u32,
576
577 pub name: String,
579
580 pub kind: String,
582}
583
584#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)]
586pub struct ContractEvent {
587 pub name: String,
589
590 pub params: Vec<ContractEventParam>,
592
593 pub signature: String,
595}
596
597#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)]
599pub struct ContractEventParam {
600 pub name: String,
602
603 pub kind: String,
605
606 pub indexed: bool,
608}
609
610#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)]
615pub struct MonitorConfig {}
616
617#[cfg(test)]
618mod tests {
619 use super::*;
620
621 #[test]
622 fn test_contract_spec_creation() {
623 let spec = ContractSpec {
624 version: "0.1.0".to_string(),
625 name: "test_program".to_string(),
626 instructions: vec![IdlInstruction {
627 name: "transfer".to_string(),
628 discriminator: Some(vec![1, 2, 3, 4, 5, 6, 7, 8]),
629 accounts: vec![],
630 args: vec![
631 IdlField {
632 name: "amount".to_string(),
633 field_type: IdlType::Primitive("u64".to_string()),
634 docs: vec![],
635 },
636 IdlField {
637 name: "recipient".to_string(),
638 field_type: IdlType::Primitive("pubkey".to_string()),
639 docs: vec![],
640 },
641 ],
642 docs: vec![],
643 }],
644 accounts: vec![],
645 events: vec![IdlEvent {
646 name: "TransferEvent".to_string(),
647 discriminator: Some(vec![1, 2, 3, 4, 5, 6, 7, 8]),
648 fields: vec![
649 IdlEventField {
650 name: "from".to_string(),
651 field_type: IdlType::Primitive("pubkey".to_string()),
652 index: true,
653 },
654 IdlEventField {
655 name: "to".to_string(),
656 field_type: IdlType::Primitive("pubkey".to_string()),
657 index: true,
658 },
659 IdlEventField {
660 name: "amount".to_string(),
661 field_type: IdlType::Primitive("u64".to_string()),
662 index: false,
663 },
664 ],
665 }],
666 types: vec![],
667 errors: vec![],
668 metadata: None,
669 };
670
671 assert_eq!(spec.name, "test_program");
672 assert_eq!(spec.instructions.len(), 1);
673 assert_eq!(spec.events.len(), 1);
674
675 let sig = spec.get_instruction_signature("transfer");
677 assert_eq!(sig, Some("transfer(u64,pubkey)".to_string()));
678
679 let event_sig = spec.get_event_signature("TransferEvent");
681 assert_eq!(
682 event_sig,
683 Some("TransferEvent(pubkey,pubkey,u64)".to_string())
684 );
685 }
686
687 #[test]
688 fn test_formatted_contract_spec() {
689 let spec = ContractSpec {
690 version: "0.1.0".to_string(),
691 name: "test_program".to_string(),
692 instructions: vec![IdlInstruction {
693 name: "transfer".to_string(),
694 discriminator: None,
695 accounts: vec![],
696 args: vec![IdlField {
697 name: "amount".to_string(),
698 field_type: IdlType::Primitive("u64".to_string()),
699 docs: vec![],
700 }],
701 docs: vec![],
702 }],
703 accounts: vec![],
704 events: vec![],
705 types: vec![],
706 errors: vec![],
707 metadata: None,
708 };
709
710 let formatted = FormattedContractSpec::from(spec);
711
712 assert_eq!(formatted.functions.len(), 1);
713 assert_eq!(formatted.functions[0].name, "transfer");
714 assert_eq!(formatted.functions[0].signature, "transfer(u64)");
715 assert_eq!(formatted.functions[0].inputs.len(), 1);
716 assert_eq!(formatted.functions[0].inputs[0].name, "amount");
717 assert_eq!(formatted.functions[0].inputs[0].kind, "u64");
718 }
719
720 #[test]
721 fn test_match_params_map() {
722 let params_map = MatchParamsMap {
723 signature: "transfer(u64,pubkey)".to_string(),
724 args: Some(vec![
725 MatchParamEntry {
726 name: "amount".to_string(),
727 value: "1000000".to_string(),
728 kind: "u64".to_string(),
729 indexed: false,
730 },
731 MatchParamEntry {
732 name: "recipient".to_string(),
733 value: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA".to_string(),
734 kind: "pubkey".to_string(),
735 indexed: false,
736 },
737 ]),
738 };
739
740 assert_eq!(params_map.signature, "transfer(u64,pubkey)");
741 let args = params_map.args.unwrap();
742 assert_eq!(args.len(), 2);
743 assert_eq!(args[0].name, "amount");
744 assert_eq!(args[0].value, "1000000");
745 }
746
747 #[test]
748 fn test_idl_type_to_string() {
749 assert_eq!(
750 idl_type_to_string(&IdlType::Primitive("u64".to_string())),
751 "u64"
752 );
753
754 assert_eq!(
755 idl_type_to_string(&IdlType::Complex(IdlTypeComplex::Vec(Box::new(
756 IdlType::Primitive("u8".to_string())
757 )))),
758 "Vec<u8>"
759 );
760
761 assert_eq!(
762 idl_type_to_string(&IdlType::Complex(IdlTypeComplex::Option(Box::new(
763 IdlType::Primitive("pubkey".to_string())
764 )))),
765 "Option<pubkey>"
766 );
767
768 assert_eq!(
769 idl_type_to_string(&IdlType::Complex(IdlTypeComplex::Array(
770 Box::new(IdlType::Primitive("u8".to_string())),
771 32
772 ))),
773 "[u8;32]"
774 );
775 }
776
777 #[test]
778 fn test_serde_serialization() {
779 let spec = ContractSpec {
780 version: "0.1.0".to_string(),
781 name: "test_program".to_string(),
782 instructions: vec![],
783 accounts: vec![],
784 events: vec![],
785 types: vec![],
786 errors: vec![],
787 metadata: None,
788 };
789
790 let serialized = serde_json::to_string(&spec).unwrap();
791 let deserialized: ContractSpec = serde_json::from_str(&serialized).unwrap();
792
793 assert_eq!(deserialized.name, "test_program");
794 assert_eq!(deserialized.version, "0.1.0");
795 }
796}