Skip to main content

anchor_litesvm/
builder.rs

1//! Builder pattern for setting up Anchor test environments
2//!
3//! This module provides builders specifically designed for Anchor programs,
4//! extending the base LiteSVM builder functionality.
5
6use crate::AnchorContext;
7use litesvm_utils::LiteSVMBuilder;
8use solana_keypair::Keypair;
9use solana_program::pubkey::Pubkey;
10use solana_signer::Signer;
11
12/// Builder for creating an AnchorContext with programs pre-deployed
13///
14/// This provides a more ergonomic way to set up Anchor test environments.
15///
16/// # Example
17/// ```ignore
18/// use anchor_litesvm::AnchorLiteSVM;
19/// use solana_program::pubkey::Pubkey;
20///
21/// // Simple single program setup
22/// let program_id = Pubkey::new_unique();
23/// let program_bytes = include_bytes!("../target/deploy/my_program.so");
24/// let mut ctx = AnchorLiteSVM::new()
25///     .deploy_program(program_id, program_bytes)
26///     .build();
27///
28/// // Or use the convenience method for single program
29/// let mut ctx = AnchorLiteSVM::build_with_program(program_id, program_bytes);
30///
31/// // Build instructions using production-compatible syntax
32/// let ix = ctx.program()
33///     .request()
34///     .accounts(...)
35///     .args(...)
36///     .instructions()?[0];
37/// ```
38pub struct AnchorLiteSVM {
39    svm_builder: LiteSVMBuilder,
40    primary_program_id: Option<Pubkey>,
41    payer: Option<Keypair>,
42}
43
44impl AnchorLiteSVM {
45    /// Create a new Anchor test environment builder
46    pub fn new() -> Self {
47        Self {
48            svm_builder: LiteSVMBuilder::new(),
49            primary_program_id: None,
50            payer: None,
51        }
52    }
53
54    /// Set the payer keypair for transactions
55    ///
56    /// If not set, a new keypair will be generated and funded.
57    pub fn with_payer(mut self, payer: Keypair) -> Self {
58        self.payer = Some(payer);
59        self
60    }
61
62    /// Add a program to be deployed
63    ///
64    /// The first program added becomes the primary program for the AnchorContext.
65    ///
66    /// # Arguments
67    ///
68    /// * `program_id` - The program ID to deploy at
69    /// * `program_bytes` - The compiled program bytes (.so file contents)
70    ///
71    /// # Example
72    ///
73    /// ```ignore
74    /// builder.deploy_program(program_id, program_bytes)
75    /// ```
76    pub fn deploy_program(mut self, program_id: Pubkey, program_bytes: &[u8]) -> Self {
77        // Set the first program as primary if not already set
78        if self.primary_program_id.is_none() {
79            self.primary_program_id = Some(program_id);
80        }
81
82        self.svm_builder = self.svm_builder.deploy_program(program_id, program_bytes);
83        self
84    }
85
86    /// Build the AnchorContext with all programs deployed
87    ///
88    /// # Returns
89    ///
90    /// Returns an AnchorContext with the primary program ID and deployed programs
91    ///
92    /// # Panics
93    ///
94    /// Panics if no programs were added
95    ///
96    /// # Example
97    ///
98    /// ```ignore
99    /// let mut ctx = builder.build();
100    /// ```
101    pub fn build(self) -> AnchorContext {
102        let program_id = self
103            .primary_program_id
104            .expect("No programs added. Call deploy_program() at least once.");
105
106        let mut svm = self.svm_builder.build();
107
108        // Create or use provided payer
109        let payer = self.payer.unwrap_or_else(|| {
110            let payer = Keypair::new();
111            // Fund the payer account
112            svm.airdrop(&payer.pubkey(), 10_000_000_000).unwrap();
113            payer
114        });
115
116        AnchorContext::new_with_payer(svm, program_id, payer)
117    }
118
119    /// Convenience method to quickly set up a single Anchor program
120    ///
121    /// This is equivalent to:
122    /// ```ignore
123    /// AnchorLiteSVM::new()
124    ///     .deploy_program(program_id, program_bytes)
125    ///     .build()
126    /// ```
127    ///
128    /// # Arguments
129    ///
130    /// * `program_id` - The program ID to deploy at
131    /// * `program_bytes` - The compiled program bytes
132    ///
133    /// # Returns
134    ///
135    /// Returns an AnchorContext with the program deployed
136    ///
137    /// # Example
138    ///
139    /// ```ignore
140    /// let mut ctx = AnchorLiteSVM::build_with_program(program_id, program_bytes);
141    /// ```
142    pub fn build_with_program(program_id: Pubkey, program_bytes: &[u8]) -> AnchorContext {
143        Self::new()
144            .deploy_program(program_id, program_bytes)
145            .build()
146    }
147
148    /// Convenience method to set up multiple programs
149    ///
150    /// The first program in the list becomes the primary program.
151    ///
152    /// # Arguments
153    ///
154    /// * `programs` - A slice of (program_id, program_bytes) tuples
155    ///
156    /// # Returns
157    ///
158    /// Returns an AnchorContext with all programs deployed
159    ///
160    /// # Example
161    ///
162    /// ```ignore
163    /// let programs = vec![
164    ///     (program_id1, program_bytes1),
165    ///     (program_id2, program_bytes2),
166    /// ];
167    /// let mut ctx = AnchorLiteSVM::build_with_programs(&programs);
168    /// ```
169    pub fn build_with_programs(programs: &[(Pubkey, &[u8])]) -> AnchorContext {
170        let mut builder = Self::new();
171        for (program_id, program_bytes) in programs {
172            builder = builder.deploy_program(*program_id, program_bytes);
173        }
174        builder.build()
175    }
176}
177
178impl Default for AnchorLiteSVM {
179    fn default() -> Self {
180        Self::new()
181    }
182}
183
184/// Extension trait for AnchorContext to provide program deployment
185pub trait ProgramTestExt {
186    /// Deploy an additional program to this context
187    ///
188    /// # Example
189    /// ```no_run
190    /// # use anchor_litesvm::{AnchorContext, ProgramTestExt};
191    /// # use litesvm::LiteSVM;
192    /// # use solana_program::pubkey::Pubkey;
193    /// # let svm = LiteSVM::new();
194    /// # let program_id = Pubkey::new_unique();
195    /// # let mut ctx = AnchorContext::new(svm, program_id);
196    /// # let other_program_id = Pubkey::new_unique();
197    /// # let other_program_bytes = vec![];
198    /// ctx.deploy_program(other_program_id, &other_program_bytes);
199    /// ```
200    fn deploy_program(&mut self, program_id: Pubkey, program_bytes: &[u8]);
201}
202
203impl ProgramTestExt for AnchorContext {
204    fn deploy_program(&mut self, program_id: Pubkey, program_bytes: &[u8]) {
205        self.svm
206            .add_program(program_id, program_bytes)
207            .expect("Failed to deploy program");
208    }
209}