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}