66 pubkey:: Pubkey ,
77 signature:: { Keypair , Signer } ,
88 } ,
9- std:: {
10- fs:: File ,
11- io:: Read ,
12- path:: Path ,
13- } ,
9+ std:: { fs:: File , io:: Read , path:: Path } ,
1410 thiserror:: Error ,
1511} ;
1612
1713/// Utility module for deploying eBPF programs to Solana Virtual Machines (SVMs)
18- ///
14+ ///
1915/// This module provides functionality to deploy eBPF programs (.so files) to
2016/// one or more SVM networks (mainnet, testnet, devnet). It handles the loading
2117/// of program binaries, keypairs, and program IDs from files, and provides
2218/// a consistent interface for deployment operations.
23- ///
19+ ///
2420/// # Example
25- ///
21+ ///
2622/// ```
2723/// let config = DeployConfig {
2824/// binary_path: "program.so".to_string(),
3228/// publish_idl: true,
3329/// network_filter: "all".to_string(),
3430/// };
35- ///
31+ ///
3632/// let results = deploy_to_all_networks(config, CommitmentConfig::confirmed()).await;
3733/// for result in results {
3834/// match result {
4137/// }
4238/// }
4339/// ```
44-
4540/// Error types for eBPF deployment operations
4641#[ derive( Error , Debug ) ]
4742pub enum EbpfDeployError {
@@ -62,13 +57,13 @@ pub enum EbpfDeployError {
6257
6358 #[ error( "Network not available: {0}" ) ]
6459 NetworkNotAvailable ( String ) ,
65-
60+
6661 #[ error( "Insufficient funds: {0}" ) ]
6762 InsufficientFunds ( String ) ,
68-
63+
6964 #[ error( "Transaction error: {0}" ) ]
7065 TransactionError ( String ) ,
71-
66+
7267 #[ error( "IDL publishing error: {0}" ) ]
7368 IdlPublishError ( String ) ,
7469}
@@ -112,28 +107,27 @@ pub fn load_program_id(path: &str) -> Result<Pubkey, EbpfDeployError> {
112107 let mut file = File :: open ( path) ?;
113108 let mut contents = String :: new ( ) ;
114109 file. read_to_string ( & mut contents) ?;
115-
110+
116111 // Try parsing as JSON with a "programId" field
117112 if let Ok ( json) = serde_json:: from_str :: < serde_json:: Value > ( & contents) {
118113 if let Some ( program_id) = json. get ( "programId" ) . and_then ( |v| v. as_str ( ) ) {
119114 return Pubkey :: try_from ( program_id)
120115 . map_err ( |_| EbpfDeployError :: InvalidProgramId ( program_id. to_string ( ) ) ) ;
121116 }
122117 }
123-
118+
124119 // If not JSON with programId field, try direct pubkey parse
125- Pubkey :: try_from ( contents. trim ( ) )
126- . map_err ( |_| EbpfDeployError :: InvalidProgramId ( contents) )
120+ Pubkey :: try_from ( contents. trim ( ) ) . map_err ( |_| EbpfDeployError :: InvalidProgramId ( contents) )
127121}
128122
129123/// Load eBPF program binary from file
130124pub fn load_program ( path : & str ) -> Result < Vec < u8 > , EbpfDeployError > {
131125 let path = Path :: new ( path) ;
132126 let mut file = File :: open ( path) ?;
133-
127+
134128 let mut program_data = Vec :: new ( ) ;
135129 file. read_to_end ( & mut program_data) ?;
136-
130+
137131 Ok ( program_data)
138132}
139133
@@ -147,26 +141,28 @@ pub async fn deploy_to_network(
147141 let program_owner = load_keypair ( & config. owner_path ) ?;
148142 let fee_payer = load_keypair ( & config. fee_payer_path ) ?;
149143 let program_data = load_program ( & config. binary_path ) ?;
150-
144+
151145 // Get network name for result
152146 let network_name = config. network_filter . clone ( ) ;
153-
147+
154148 println ! ( "Deploying to {} network..." , network_name) ;
155149 println ! ( " Program ID: {}" , program_id) ;
156150 println ! ( " Owner: {}" , program_owner. pubkey( ) ) ;
157151 println ! ( " Fee payer: {}" , fee_payer. pubkey( ) ) ;
158152 println ! ( " Binary size: {} bytes" , program_data. len( ) ) ;
159-
153+
160154 // Attempt to deploy the program
161155 let result = match deploy_bpf_program (
162- client,
163- & fee_payer,
164- & program_owner,
165- program_id,
156+ client,
157+ & fee_payer,
158+ & program_owner,
159+ program_id,
166160 & program_data,
167161 commitment_config,
168162 config. publish_idl ,
169- ) . await {
163+ )
164+ . await
165+ {
170166 Ok ( signature) => {
171167 println ! ( "✅ Deployment successful on {}" , network_name) ;
172168 DeploymentResult {
@@ -176,7 +172,7 @@ pub async fn deploy_to_network(
176172 transaction_signature : Some ( signature) ,
177173 error_message : None ,
178174 }
179- } ,
175+ }
180176 Err ( e) => {
181177 println ! ( "❌ Deployment failed on {}: {}" , network_name, e) ;
182178 DeploymentResult {
@@ -188,7 +184,7 @@ pub async fn deploy_to_network(
188184 }
189185 }
190186 } ;
191-
187+
192188 Ok ( result)
193189}
194190
@@ -211,15 +207,17 @@ async fn deploy_bpf_program(
211207 return Err ( EbpfDeployError :: ClientError ( err) ) ;
212208 }
213209 }
214-
210+
215211 // Check fee payer balance
216212 let balance = client. get_balance ( & fee_payer. pubkey ( ) ) ?;
217- if balance < 10_000_000 { // 0.01 SOL minimum
218- return Err ( EbpfDeployError :: InsufficientFunds (
219- format ! ( "Fee payer has insufficient balance: {} lamports" , balance)
220- ) ) ;
213+ if balance < 10_000_000 {
214+ // 0.01 SOL minimum
215+ return Err ( EbpfDeployError :: InsufficientFunds ( format ! (
216+ "Fee payer has insufficient balance: {} lamports" ,
217+ balance
218+ ) ) ) ;
221219 }
222-
220+
223221 // In a real implementation, this function would:
224222 // 1. Create a BPF loader instruction to deploy the program
225223 // 2. Create and sign a transaction with that instruction
@@ -228,7 +226,7 @@ async fn deploy_bpf_program(
228226
229227 // For now, simulate with a small delay to represent network operation
230228 tokio:: time:: sleep ( tokio:: time:: Duration :: from_millis ( 500 ) ) . await ;
231-
229+
232230 // In a real implementation, return the actual transaction signature
233231 Ok ( "simulated_transaction_signature_for_deployment" . to_string ( ) )
234232}
@@ -239,32 +237,37 @@ pub async fn deploy_to_all_networks(
239237 commitment_config : CommitmentConfig ,
240238) -> Vec < Result < DeploymentResult , EbpfDeployError > > {
241239 let mut results = Vec :: new ( ) ;
242-
240+
243241 // Determine which networks to deploy to based on the filter
244242 let networks = match config. network_filter . to_lowercase ( ) . as_str ( ) {
245243 "mainnet" => vec ! [ NetworkType :: Mainnet ] ,
246244 "testnet" => vec ! [ NetworkType :: Testnet ] ,
247245 "devnet" => vec ! [ NetworkType :: Devnet ] ,
248- "all" => vec ! [ NetworkType :: Mainnet , NetworkType :: Testnet , NetworkType :: Devnet ] ,
246+ "all" => vec ! [
247+ NetworkType :: Mainnet ,
248+ NetworkType :: Testnet ,
249+ NetworkType :: Devnet ,
250+ ] ,
249251 _ => {
250252 // Invalid network filter, return error
251- let error = EbpfDeployError :: NetworkNotAvailable (
252- format ! ( "Invalid network filter: {}" , config. network_filter)
253- ) ;
253+ let error = EbpfDeployError :: NetworkNotAvailable ( format ! (
254+ "Invalid network filter: {}" ,
255+ config. network_filter
256+ ) ) ;
254257 return vec ! [ Err ( error) ] ;
255258 }
256259 } ;
257-
260+
258261 for network in networks {
259262 // Create client for the specific network
260263 let client_url = match network {
261264 NetworkType :: Mainnet => "https://api.mainnet-beta.solana.com" ,
262265 NetworkType :: Testnet => "https://api.testnet.solana.com" ,
263266 NetworkType :: Devnet => "https://api.devnet.solana.com" ,
264267 } ;
265-
268+
266269 let client = RpcClient :: new ( client_url. to_string ( ) ) ;
267-
270+
268271 // Create network-specific config
269272 let network_config = DeployConfig {
270273 network_filter : match network {
@@ -274,11 +277,11 @@ pub async fn deploy_to_all_networks(
274277 } ,
275278 ..config. clone ( )
276279 } ;
277-
280+
278281 // Deploy to this network
279282 let result = deploy_to_network ( & client, & network_config, commitment_config) . await ;
280283 results. push ( result) ;
281284 }
282-
285+
283286 results
284- }
287+ }
0 commit comments