Skip to main content

surfpool_sdk/cheatcodes/builders/
deploy_program.rs

1use std::path::{Path, PathBuf};
2
3use solana_pubkey::Pubkey;
4
5use crate::{
6    cheatcodes::read_keypair_pubkey,
7    error::{SurfnetError, SurfnetResult},
8};
9
10/// Builder for deploying a program to Surfnet.
11///
12/// Unlike single-RPC cheatcode builders, deployment is a compound operation:
13/// it writes the program bytes first and then optionally registers an IDL.
14///
15/// ```rust
16/// use surfpool_sdk::{Pubkey, Surfnet};
17/// use surfpool_sdk::cheatcodes::builders::DeployProgram;
18///
19/// # async fn example() {
20/// let surfnet = Surfnet::start().await.unwrap();
21/// let cheats = surfnet.cheatcodes();
22/// let program_id = Pubkey::new_unique();
23///
24/// cheats
25///     .deploy(
26///         DeployProgram::new(program_id)
27///             .so_path("target/deploy/my_program.so")
28///             .idl_path("target/idl/my_program.json"),
29///     )
30///     .unwrap();
31/// # }
32/// ```
33pub struct DeployProgram {
34    program_id: Pubkey,
35    so_path: Option<PathBuf>,
36    so_bytes: Option<Vec<u8>>,
37    idl_path: Option<PathBuf>,
38}
39
40impl DeployProgram {
41    /// Create a deployment builder from a known program id.
42    pub fn new(program_id: Pubkey) -> Self {
43        Self {
44            program_id,
45            so_path: None,
46            so_bytes: None,
47            idl_path: None,
48        }
49    }
50
51    /// Create a deployment builder from a Solana keypair file.
52    ///
53    /// The program id is derived from the keypair public key.
54    pub fn from_keypair_path(path: impl AsRef<Path>) -> SurfnetResult<Self> {
55        let path = path.as_ref();
56        let program_id = read_keypair_pubkey(path)?;
57        Ok(Self::new(program_id))
58    }
59
60    /// Set the path to the `.so` artifact to deploy.
61    pub fn so_path(mut self, path: impl Into<PathBuf>) -> Self {
62        self.so_path = Some(path.into());
63        self
64    }
65
66    /// Set the raw `.so` bytes directly.
67    pub fn so_bytes(mut self, bytes: Vec<u8>) -> Self {
68        self.so_bytes = Some(bytes);
69        self
70    }
71
72    /// Set the path to an Anchor IDL JSON file to register after deployment.
73    pub fn idl_path(mut self, path: impl Into<PathBuf>) -> Self {
74        self.idl_path = Some(path.into());
75        self
76    }
77
78    /// Set the IDL path only if the file exists.
79    pub(crate) fn idl_path_if_exists(mut self, path: impl Into<PathBuf>) -> Self {
80        let path = path.into();
81        if path.exists() {
82            self.idl_path = Some(path);
83        }
84        self
85    }
86
87    /// Return the program id that will be deployed.
88    pub(crate) fn program_id(&self) -> Pubkey {
89        self.program_id
90    }
91
92    /// Resolve the program bytes from either an explicit path or inline bytes.
93    pub(crate) fn load_so_bytes(&self) -> SurfnetResult<Vec<u8>> {
94        match (&self.so_bytes, &self.so_path) {
95            (Some(bytes), _) => Ok(bytes.clone()),
96            (None, Some(path)) => std::fs::read(path).map_err(|e| {
97                SurfnetError::Cheatcode(format!(
98                    "failed to read program bytes from {}: {e}",
99                    path.display()
100                ))
101            }),
102            (None, None) => Err(SurfnetError::Cheatcode(
103                "deploy program requires either so_path or so_bytes".to_string(),
104            )),
105        }
106    }
107
108    /// Resolve and parse the optional IDL file.
109    pub(crate) fn load_idl(&self) -> SurfnetResult<Option<surfpool_types::Idl>> {
110        let Some(path) = &self.idl_path else {
111            return Ok(None);
112        };
113
114        let contents = std::fs::read_to_string(path).map_err(|e| {
115            SurfnetError::Cheatcode(format!("failed to read IDL from {}: {e}", path.display()))
116        })?;
117        let idl = serde_json::from_str(&contents).map_err(|e| {
118            SurfnetError::Cheatcode(format!("failed to parse IDL from {}: {e}", path.display()))
119        })?;
120        Ok(Some(idl))
121    }
122}