soon-migrate 0.2.1

CLI tool to migrate Solana Anchor projects to the SOON Network.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
//! Oracle detection and analysis module for the SOON Network migration tool.
//!
//! This module provides functionality to detect and analyze oracle usage in Solana programs,
//! with support for various oracle providers like Pyth, Switchboard, and Chainlink.

use crate::errors::MigrationError;
use colored::*;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fs;
use std::path::Path;

/// Represents a detected oracle usage in the codebase.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OracleDetection {
    /// Type of the detected oracle
    pub oracle_type: OracleType,
    /// Confidence level of the detection
    pub confidence: ConfidenceLevel,
    /// Locations in the code where this oracle was detected
    pub locations: Vec<DetectionLocation>,
 /// Suggested migration steps for this oracle
    pub migration_suggestion: String,
}

/// Types of oracles that can be detected in the codebase.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OracleType {
    /// Pyth Network oracle
    Pyth,
    /// Switchboard oracle
    Switchboard,
    /// Chainlink oracle
    Chainlink,
    /// DIA oracle
    DIA,
    /// RedStone oracle
    RedStone,
    /// APRO oracle
    APRO,
    /// Unknown oracle type
    Unknown,
}

/// Confidence level for oracle detections
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ConfidenceLevel {
    /// High confidence - Found in dependencies and code usage
    High,
    /// Medium confidence - Found in dependencies OR clear code patterns
    Medium,
    /// Low confidence - Found in comments or weak patterns
    Low,
}

/// Represents a location in the source code where an oracle was detected
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DetectionLocation {
    /// Path to the file where the oracle was detected
    pub file_path: String,
    /// Line number where the oracle was detected (if available)
    pub line_number: Option<usize>,
    /// The code pattern that was matched
    pub pattern_matched: String,
    /// Additional context about the detection
    pub context: String,
}

/// A report containing all detected oracles and migration recommendations
#[derive(Debug, Serialize, Deserialize)]
pub struct OracleReport {
    /// List of all detected oracles in the project
    pub detected_oracles: Vec<OracleDetection>,
    /// List of recommendations for migrating the detected oracles
    pub migration_recommendations: Vec<String>,
    /// Optional APRO integration guide for the detected oracles
    pub apro_integration_guide: Option<String>,
}

/// Detector for identifying oracle usage in Solana programs
pub struct OracleDetector;

impl OracleDetector {
    /// Scans a Solana project for oracle usage and generates a detailed report.
    ///
    /// This function analyzes the project's source code and dependencies to detect
    /// any oracle integrations, such as Pyth, Switchboard, or Chainlink.
    ///
    /// # Arguments
    /// * `path` - Path to the root directory of the Solana project
    /// * `verbose` - Whether to enable verbose output during scanning
    ///
    /// # Returns
    /// A `Result` containing an `OracleReport` with the scan results, or a `MigrationError`
    /// if the scan fails.
    ///
    /// # Examples
    /// ```no_run
    /// use soon_migrate::oracle::OracleDetector;
    ///
    /// let report = OracleDetector::scan_project("./my_project", true).unwrap();
    /// println!("Found {} oracle usages", report.detected_oracles.len());
    /// ```
    pub fn scan_project(path: &str, verbose: bool) -> Result<OracleReport, MigrationError> {
        if verbose {
            println!("{}", "Scanning project for oracle usage...".cyan());
        }

        let mut detected_oracles = Vec::new();
        
        // Scan Cargo.toml for oracle dependencies
        let cargo_detections = Self::scan_cargo_toml(path)?;
        detected_oracles.extend(cargo_detections);

        // Scan Rust files for oracle imports and usage
        let code_detections = Self::scan_rust_files(path)?;
        detected_oracles.extend(code_detections);

        // Merge and deduplicate detections
        let merged_detections = Self::merge_detections(detected_oracles);
        
        // Generate migration recommendations
        let recommendations = Self::generate_recommendations(&merged_detections);
        
        // Generate APRO integration guide if oracles detected
        let apro_guide = if !merged_detections.is_empty() {
            Some(Self::generate_apro_guide(&merged_detections))
        } else {
            None
        };

        Ok(OracleReport {
            detected_oracles: merged_detections,
            migration_recommendations: recommendations,
            apro_integration_guide: apro_guide,
        })
    }

    /// Scan Cargo.toml for oracle-related dependencies
    ///
    /// # Arguments
    /// * `path` - Path to the project directory
    ///
    /// # Returns
    /// A `Result` containing a list of oracle detections or an error
    fn scan_cargo_toml(path: &str) -> Result<Vec<OracleDetection>, MigrationError> {
        let cargo_path = Path::new(path).join("Cargo.toml");
        if !cargo_path.exists() {
            return Ok(Vec::new());
        }

        let content = fs::read_to_string(&cargo_path)
            .map_err(|e| MigrationError::OracleDetectionFailed(format!("Failed to read Cargo.toml: {}", e)))?;

        let mut detections = Vec::new();

        // Pyth detection
        if content.contains("pyth-solana-receiver-sdk") {
            detections.push(OracleDetection {
                oracle_type: OracleType::Pyth,
                confidence: ConfidenceLevel::High,
                locations: vec![DetectionLocation {
                    file_path: "Cargo.toml".to_string(),
                    line_number: Self::find_line_number(&content, "pyth-solana-receiver-sdk"),
                    pattern_matched: "pyth-solana-receiver-sdk".to_string(),
                    context: "Cargo.toml dependency".to_string(),
                }],
                migration_suggestion: "Consider migrating to APRO oracle for SOON Network compatibility. APRO provides similar price feed functionality with better performance.".to_string(),
            });
        }

        // Switchboard detection
        if content.contains("switchboard-v2") || content.contains("switchboard_on_demand") {
            let pattern = if content.contains("switchboard-v2") { "switchboard-v2" } else { "switchboard_on_demand" };
            detections.push(OracleDetection {
                oracle_type: OracleType::Switchboard,
                confidence: ConfidenceLevel::High,
                locations: vec![DetectionLocation {
                    file_path: "Cargo.toml".to_string(),
                    line_number: Self::find_line_number(&content, pattern),
                    pattern_matched: pattern.to_string(),
                    context: "Cargo.toml dependency".to_string(),
                }],
                migration_suggestion: "APRO oracle offers customizable data feeds similar to Switchboard with enhanced performance on SOON Network.".to_string(),
            });
        }

        // Chainlink detection
        if content.contains("chainlink_solana") {
            detections.push(OracleDetection {
                oracle_type: OracleType::Chainlink,
                confidence: ConfidenceLevel::High,
                locations: vec![DetectionLocation {
                    file_path: "Cargo.toml".to_string(),
                    line_number: Self::find_line_number(&content, "chainlink_solana"),
                    pattern_matched: "chainlink_solana".to_string(),
                    context: "Cargo.toml dependency".to_string(),
                }],
                migration_suggestion: "APRO oracle provides reliable price feeds compatible with Chainlink's API patterns for seamless migration.".to_string(),
            });
        }

        Ok(detections)
    }

    /// Scan Rust source files for oracle usage patterns
    ///
    /// # Arguments
    /// * `path` - Path to the project directory
    ///
    /// # Returns
    /// A `Result` containing a list of oracle detections or an error
    fn scan_rust_files(path: &str) -> Result<Vec<OracleDetection>, MigrationError> {
        let mut detections = Vec::new();
        
        // Walk through all .rs files
        Self::walk_directory(Path::new(path), &mut |file_path| {
            if let Some(extension) = file_path.extension() {
                if extension == "rs" {
                    if let Ok(content) = fs::read_to_string(file_path) {
                        detections.extend(Self::scan_rust_content(&content, file_path)?);
                    }
                }
            }
            Ok(())
        })?;

        Ok(detections)
    }

    /// Scan Rust source code content for oracle patterns
    ///
    /// # Arguments
    /// * `content` - The Rust source code to scan
    /// * `file_path` - Path to the source file (for reporting)
    ///
    /// # Returns
    /// A `Result` containing a list of oracle detections or an error
    fn scan_rust_content(content: &str, file_path: &Path) -> Result<Vec<OracleDetection>, MigrationError> {
        let mut detections = Vec::new();
        let file_path_str = file_path.to_string_lossy().to_string();

        // Pyth patterns
        let pyth_patterns = [
            "use pyth_solana_receiver_sdk",
            "PriceUpdateV2",
            "get_price_no_older_than",
            "pyth_solana_receiver_sdk::",
        ];

        for pattern in &pyth_patterns {
            if content.contains(pattern) {
                detections.push(OracleDetection {
                    oracle_type: OracleType::Pyth,
                    confidence: ConfidenceLevel::High,
                    locations: vec![DetectionLocation {
                        file_path: file_path_str.clone(),
                        line_number: Self::find_line_number(content, pattern),
                        pattern_matched: pattern.to_string(),
                        context: format!("Rust code usage: {}", pattern),
                    }],
                    migration_suggestion: "Replace Pyth price feeds with APRO oracle integration. See APRO documentation for migration guide.".to_string(),
                });
                break; // Only add one detection per oracle type per file
            }
        }

        // Switchboard patterns
        let switchboard_patterns = [
            "use switchboard_v2",
            "use switchboard_on_demand",
            "AggregatorAccountData",
            "get_result",
            "switchboard_v2::",
        ];

        for pattern in &switchboard_patterns {
            if content.contains(pattern) {
                detections.push(OracleDetection {
                    oracle_type: OracleType::Switchboard,
                    confidence: ConfidenceLevel::High,
                    locations: vec![DetectionLocation {
                        file_path: file_path_str.clone(),
                        line_number: Self::find_line_number(content, pattern),
                        pattern_matched: pattern.to_string(),
                        context: format!("Rust code usage: {}", pattern),
                    }],
                    migration_suggestion: "Replace Switchboard aggregators with APRO oracle data feeds for SOON Network compatibility.".to_string(),
                });
                break;
            }
        }

        // Chainlink patterns
        let chainlink_patterns = [
            "use chainlink_solana",
            "latest_round_data",
            "chainlink_solana::",
            "chainlink::",
        ];

        for pattern in &chainlink_patterns {
            if content.contains(pattern) {
                detections.push(OracleDetection {
                    oracle_type: OracleType::Chainlink,
                    confidence: ConfidenceLevel::High,
                    locations: vec![DetectionLocation {
                        file_path: file_path_str.clone(),
                        line_number: Self::find_line_number(content, pattern),
                        pattern_matched: pattern.to_string(),
                        context: format!("Rust code usage: {}", pattern),
                    }],
                    migration_suggestion: "Migrate Chainlink price feeds to APRO oracle for enhanced performance on SOON Network.".to_string(),
                });
                break;
            }
        }

        // DIA patterns (weaker detection)
        let dia_patterns = [
            "CoinInfo",
            "// DIA",
            "dia oracle",
            "DIA Oracle",
        ];

        for pattern in &dia_patterns {
            if content.to_lowercase().contains(&pattern.to_lowercase()) {
                detections.push(OracleDetection {
                    oracle_type: OracleType::DIA,
                    confidence: ConfidenceLevel::Low,
                    locations: vec![DetectionLocation {
                        file_path: file_path_str.clone(),
                        line_number: Self::find_line_number(content, pattern),
                        pattern_matched: pattern.to_string(),
                        context: format!("Potential DIA usage: {}", pattern),
                    }],
                    migration_suggestion: "If using DIA oracle, consider migrating to APRO for better SOON Network integration.".to_string(),
                });
                break;
            }
        }

        // RedStone patterns (weaker detection)
        let redstone_patterns = [
            "redstone",
            "RedStone",
            "// RedStone",
            "wormhole",
        ];

        for pattern in &redstone_patterns {
            if content.contains(pattern) {
                detections.push(OracleDetection {
                    oracle_type: OracleType::RedStone,
                    confidence: ConfidenceLevel::Low,
                    locations: vec![DetectionLocation {
                        file_path: file_path_str.clone(),
                        line_number: Self::find_line_number(content, pattern),
                        pattern_matched: pattern.to_string(),
                        context: format!("Potential RedStone usage: {}", pattern),
                    }],
                    migration_suggestion: "If using RedStone oracle, APRO provides similar RWA oracle capabilities for SOON Network.".to_string(),
                });
                break;
            }
        }

        Ok(detections)
    }

    /// Recursively walk a directory and apply a callback to each file
    ///
    /// # Arguments
    /// * `dir` - Directory to walk
    /// * `callback` - Callback function to apply to each file
    ///
    /// # Returns
    /// A `Result` indicating success or an error
    fn walk_directory<F>(dir: &Path, callback: &mut F) -> Result<(), MigrationError>
    where
        F: FnMut(&Path) -> Result<(), MigrationError>,
    {
        if dir.is_dir() {
            let entries = fs::read_dir(dir)
                .map_err(|e| MigrationError::OracleDetectionFailed(format!("Failed to read directory: {}", e)))?;
            
            for entry in entries {
                let entry = entry
                    .map_err(|e| MigrationError::OracleDetectionFailed(format!("Failed to read directory entry: {}", e)))?;
                let path = entry.path();
                
                if path.is_dir() {
                    // Skip common directories that won't contain oracle code
                    if let Some(dir_name) = path.file_name() {
                        let dir_str = dir_name.to_string_lossy();
                        if dir_str == "target" || dir_str == "node_modules" || dir_str == ".git" {
                            continue;
                        }
                    }
                    Self::walk_directory(&path, callback)?;
                } else {
                    callback(&path)?;
                }
            }
        }
        Ok(())
    }

    /// Find the line number of a pattern in a string
    ///
    /// # Arguments
    /// * `content` - The content to search in
    /// * `pattern` - The pattern to search for
    ///
    /// # Returns
    /// The line number where the pattern was found, or `None` if not found
    fn find_line_number(content: &str, pattern: &str) -> Option<usize> {
        content
            .lines()
            .enumerate()
            .find(|(_, line)| line.contains(pattern))
            .map(|(i, _)| i + 1)
    }

    /// Merge duplicate oracle detections
    ///
    /// # Arguments
    /// * `detections` - List of detections to merge
    ///
    /// # Returns
    /// A deduplicated list of detections with merged locations
    fn merge_detections(detections: Vec<OracleDetection>) -> Vec<OracleDetection> {
        let mut oracle_map: HashMap<String, OracleDetection> = HashMap::new();
        
        for detection in detections {
            let key = format!("{:?}", detection.oracle_type);
            
            if let Some(existing) = oracle_map.get_mut(&key) {
                // Merge locations
                existing.locations.extend(detection.locations);
                // Use highest confidence
                if matches!(detection.confidence, ConfidenceLevel::High) {
                    existing.confidence = ConfidenceLevel::High;
                } else if matches!(detection.confidence, ConfidenceLevel::Medium) && matches!(existing.confidence, ConfidenceLevel::Low) {
                    existing.confidence = ConfidenceLevel::Medium;
                }
            } else {
                oracle_map.insert(key, detection);
            }
        }
        
        oracle_map.into_values().collect()
    }

    /// Generate migration recommendations based on detected oracles
    ///
    /// # Arguments
    /// * `detections` - List of detected oracles
    ///
    /// # Returns
    /// A list of migration recommendations
    fn generate_recommendations(detections: &[OracleDetection]) -> Vec<String> {
        let mut recommendations = Vec::new();
        
        if detections.is_empty() {
            recommendations.push("No oracle usage detected. Your project should migrate smoothly to SOON Network.".to_string());
            return recommendations;
        }

        recommendations.push("🔍 Oracle usage detected in your project. Consider these migration steps:".to_string());
        recommendations.push("".to_string());

        for detection in detections {
            let confidence_icon = match detection.confidence {
                ConfidenceLevel::High => "🔴",
                ConfidenceLevel::Medium => "🟡",
                ConfidenceLevel::Low => "🟢",
            };
            
            recommendations.push(format!("{} {:?} Oracle detected with {} confidence", 
                confidence_icon, detection.oracle_type, 
                format!("{:?}", detection.confidence).to_lowercase()));
            
            for location in &detection.locations {
                if let Some(line_num) = location.line_number {
                    recommendations.push(format!("   📁 {}:{} - {}", location.file_path, line_num, location.pattern_matched));
                } else {
                    recommendations.push(format!("   📁 {} - {}", location.file_path, location.pattern_matched));
                }
            }
            recommendations.push(format!("   💡 {}", detection.migration_suggestion));
            recommendations.push("".to_string());
        }

        recommendations.push("📚 Next steps:".to_string());
        recommendations.push("1. Review the APRO oracle integration guide generated below".to_string());
        recommendations.push("2. Update your dependencies to use APRO oracle SDK".to_string());
        recommendations.push("3. Replace oracle-specific code with APRO equivalents".to_string());
        recommendations.push("4. Test your price feed integrations on SOON devnet".to_string());

        recommendations
    }

    /// Generate an APRO integration guide for the detected oracles
    ///
    /// # Arguments
    /// * `detections` - List of detected oracles
    ///
    /// # Returns
    /// A formatted APRO integration guide
    fn generate_apro_guide(detections: &[OracleDetection]) -> String {
        let mut guide = String::new();
        
        guide.push_str("# APRO Oracle Integration Guide for SOON Network\n\n");
        guide.push_str("## Overview\n");
        guide.push_str("APRO has chosen SOON as their first SVM chain to support oracle services. ");
        guide.push_str("This guide will help you migrate your existing oracle integrations.\n\n");
        
        guide.push_str("## Program IDs\n");
        guide.push_str("```\n");
        guide.push_str("Devnet:  4Mvy4RKRyJMf4PHavvGUuTj9agoddUZ9atQoFma1tyMY\n");
        guide.push_str("Mainnet: 4Mvy4RKRyJMf4PHavvGUuTj9agoddUZ9atQoFma1tyMY\n");
        guide.push_str("```\n\n");
        
        guide.push_str("## API Endpoints\n");
        guide.push_str("```\n");
        guide.push_str("Devnet:  https://live-api-test.apro.com\n");
        guide.push_str("Mainnet: https://live-api.apro.com\n");
        guide.push_str("```\n\n");

        // Add specific migration examples based on detected oracles
        for detection in detections {
            match detection.oracle_type {
                OracleType::Pyth => {
                    guide.push_str("## Migrating from Pyth\n");
                    guide.push_str("Replace your Pyth price feed calls:\n");
                    guide.push_str("```rust\n");
                    guide.push_str("// Before (Pyth)\n");
                    guide.push_str("use pyth_solana_receiver_sdk::PriceUpdateV2;\n");
                    guide.push_str("let price = price_update.get_price_no_older_than(&clock, max_age)?;\n\n");
                    guide.push_str("// After (APRO)\n");
                    guide.push_str("use oracle_sdk::load_price_feed_from_account_info;\n");
                    guide.push_str("let price_feed = load_price_feed_from_account_info(&price_account)?;\n");
                    guide.push_str("let price = price_feed.benchmark_price;\n");
                    guide.push_str("```\n\n");
                }
                OracleType::Switchboard => {
                    guide.push_str("## Migrating from Switchboard\n");
                    guide.push_str("Replace your Switchboard aggregator calls:\n");
                    guide.push_str("```rust\n");
                    guide.push_str("// Before (Switchboard)\n");
                    guide.push_str("use switchboard_v2::AggregatorAccountData;\n");
                    guide.push_str("let result = aggregator.get_result()?;\n\n");
                    guide.push_str("// After (APRO)\n");
                    guide.push_str("use oracle_sdk::load_price_feed_from_account_info;\n");
                    guide.push_str("let price_feed = load_price_feed_from_account_info(&price_account)?;\n");
                    guide.push_str("let result = price_feed.benchmark_price;\n");
                    guide.push_str("```\n\n");
                }
                OracleType::Chainlink => {
                    guide.push_str("## Migrating from Chainlink\n");
                    guide.push_str("Replace your Chainlink price feed calls:\n");
                    guide.push_str("```rust\n");
                    guide.push_str("// Before (Chainlink)\n");
                    guide.push_str("use chainlink_solana as chainlink;\n");
                    guide.push_str("let round_data = chainlink::latest_round_data(ctx, &feed_account)?;\n\n");
                    guide.push_str("// After (APRO)\n");
                    guide.push_str("use oracle_sdk::load_price_feed_from_account_info;\n");
                    guide.push_str("let price_feed = load_price_feed_from_account_info(&price_account)?;\n");
                    guide.push_str("let price = price_feed.benchmark_price;\n");
                    guide.push_str("```\n\n");
                }
                _ => {}
            }
        }

        guide.push_str("## Available Price Feeds (Devnet)\n");
        guide.push_str("- BTC/USD: 0x0003665949c883f9e0f6f002eac32e00bd59dfe6c34e92a91c37d6a8322d6489\n");
        guide.push_str("- ETH/USD: 0x0003555ace6b39aae1b894097d0a9fc17f504c62fea598fa206cc6f5088e6e45\n");
        guide.push_str("- SOL/USD: 0x000343ec7f6691d6bf679978bab5c093fa45ee74c0baac6cc75649dc59cc21d3\n");
        guide.push_str("- USDT/USD: 0x00039a0c0be4e43cacda1599ac414205651f4a62b614b6be9e5318a182c33eb0\n");
        guide.push_str("- USDC/USD: 0x00034b881a0c0fff844177f881a313ff894bfc6093d33b5514e34d7faa41b7ef\n\n");

        guide.push_str("## Getting Started\n");
        guide.push_str("1. Contact APRO BD team for authorization:\n");
        guide.push_str("   - Email: bd@apro.com\n");
        guide.push_str("   - Telegram: Head of Business Development\n");
        guide.push_str("2. Add APRO oracle SDK to your Cargo.toml\n");
        guide.push_str("3. Update your price feed integration code\n");
        guide.push_str("4. Test on SOON devnet before mainnet deployment\n\n");

        guide.push_str("For detailed integration examples, see the complete APRO documentation.\n");

        guide
    }

    /// Print a formatted oracle detection report
    ///
    /// # Arguments
    /// * `report` - The oracle report to print
    /// * `verbose` - Whether to include detailed information
    pub fn print_report(report: &OracleReport, verbose: bool) {
        println!("{}", "=== Oracle Detection Report ===".bold().cyan());
        println!();

        if report.detected_oracles.is_empty() {
            println!("{} No oracle usage detected", "".green());
            println!("Your project should migrate smoothly to SOON Network without oracle changes.");
            return;
        }

        println!("{} {} oracle(s) detected:", "🔍".yellow(), report.detected_oracles.len());
        println!();

        for detection in &report.detected_oracles {
            let confidence_color = match detection.confidence {
                ConfidenceLevel::High => "red",
                ConfidenceLevel::Medium => "yellow", 
                ConfidenceLevel::Low => "green",
            };

            println!("{} {:?} Oracle ({})", 
                match detection.confidence {
                    ConfidenceLevel::High => "🔴",
                    ConfidenceLevel::Medium => "🟡",
                    ConfidenceLevel::Low => "🟢",
                },
                detection.oracle_type,
                format!("{:?} confidence", detection.confidence).color(confidence_color)
            );

            if verbose {
                for location in &detection.locations {
                    if let Some(line) = location.line_number {
                        println!("  📁 {}:{} - {}", location.file_path, line, location.pattern_matched);
                    } else {
                        println!("  📁 {} - {}", location.file_path, location.pattern_matched);
                    }
                }
            }

            println!("  💡 {}", detection.migration_suggestion);
            println!();
        }

        println!("{}", "=== Migration Recommendations ===".bold().cyan());
        for rec in &report.migration_recommendations {
            if rec.starts_with("🔍") || rec.starts_with("📚") {
                println!("{}", rec.bold());
            } else if rec.is_empty() {
                println!();
            } else {
                println!("{}", rec);
            }
        }

        if report.apro_integration_guide.is_some() {
            println!();
            println!("{}", "💡 Run with --show-guide to see the complete APRO integration guide".yellow());
        }
    }

    /// Print the APRO integration guide if available
    ///
    /// # Arguments
    /// * `report` - The oracle report containing the guide
    pub fn print_integration_guide(report: &OracleReport) {
        if let Some(guide) = &report.apro_integration_guide {
            println!("{}", guide);
        } else {
            println!("No oracle integration guide available.");
        }
    }
}

impl std::fmt::Display for OracleType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            OracleType::Pyth => write!(f, "Pyth"),
            OracleType::Switchboard => write!(f, "Switchboard"), 
            OracleType::Chainlink => write!(f, "Chainlink"),
            OracleType::DIA => write!(f, "DIA"),
            OracleType::RedStone => write!(f, "RedStone"),
            OracleType::APRO => write!(f, "APRO"),
            OracleType::Unknown => write!(f, "Unknown"),
        }
    }
}