11use super :: { command:: CommandResponse , explorer_link:: OutputLink } ;
22use crate :: helpers:: block_explorer:: LinkProvider ;
33use crate :: response:: cast_message:: SncastMessage ;
4+ use anyhow:: Error ;
5+ use camino:: Utf8PathBuf ;
46use conversions:: string:: IntoHexStr ;
57use conversions:: { padded_felt:: PaddedFelt , serde:: serialize:: CairoSerialize } ;
68use foundry_ui:: Message ;
@@ -9,6 +11,8 @@ use indoc::formatdoc;
911use serde:: { Deserialize , Serialize } ;
1012use serde_json:: Value ;
1113use serde_json:: json;
14+ use starknet:: core:: types:: contract:: { AbiConstructor , AbiEntry } ;
15+ use std:: fmt:: Write ;
1216#[ derive( Clone , Serialize , Deserialize , CairoSerialize , Debug , PartialEq ) ]
1317pub struct DeclareTransactionResponse {
1418 pub class_hash : PaddedFelt ,
@@ -129,3 +133,118 @@ impl OutputLink for DeclareTransactionResponse {
129133 )
130134 }
131135}
136+
137+ #[ derive( Clone , Serialize , Deserialize , Debug , PartialEq ) ]
138+ pub struct DeployCommandMessage {
139+ accounts_file : Option < String > ,
140+ account : String ,
141+ class_hash : PaddedFelt ,
142+ arguments_flag : Option < String > ,
143+ network_flag : String ,
144+ }
145+
146+ impl DeployCommandMessage {
147+ pub fn new (
148+ abi : & [ AbiEntry ] ,
149+ response : & DeclareTransactionResponse ,
150+ account : & str ,
151+ accounts_file : & Utf8PathBuf ,
152+ network_flag : String ,
153+ ) -> Result < Self , Error > {
154+ let arguments_flag: Option < String > = generate_arguments_flag ( abi) ;
155+ let accounts_file_str = accounts_file. to_string ( ) ;
156+ let accounts_file = ( !accounts_file_str
157+ . contains ( "starknet_accounts/starknet_open_zeppelin_accounts.json" ) )
158+ . then_some ( accounts_file_str) ;
159+
160+ Ok ( Self {
161+ account : account. to_string ( ) ,
162+ accounts_file,
163+ class_hash : response. class_hash ,
164+ arguments_flag,
165+ network_flag,
166+ } )
167+ }
168+ }
169+
170+ impl Message for DeployCommandMessage {
171+ fn text ( & self ) -> String {
172+ let mut command = String :: from ( "sncast" ) ;
173+
174+ let accounts_file_flag = generate_accounts_file_flag ( self . accounts_file . as_ref ( ) ) ;
175+ if let Some ( flag) = accounts_file_flag {
176+ write ! ( command, " {flag}" ) . unwrap ( ) ;
177+ }
178+
179+ let account_flag = format ! ( "--account {}" , self . account) ;
180+ write ! ( command, " {account_flag}" ) . unwrap ( ) ;
181+
182+ write ! ( command, " deploy" ) . unwrap ( ) ;
183+
184+ write ! (
185+ command,
186+ " --class-hash {}" ,
187+ self . class_hash. into_hex_string( )
188+ )
189+ . unwrap ( ) ;
190+
191+ if let Some ( arguments) = & self . arguments_flag {
192+ write ! ( command, " {arguments}" ) . unwrap ( ) ;
193+ }
194+
195+ write ! ( command, " {}" , self . network_flag) . unwrap ( ) ;
196+
197+ let header = if self . arguments_flag . is_some ( ) {
198+ "To deploy a contract of this class, replace the placeholders in `--arguments` with your actual values, then run:"
199+ } else {
200+ "To deploy a contract of this class, run:"
201+ } ;
202+
203+ formatdoc ! (
204+ "
205+ {header}
206+ {command}
207+ "
208+ )
209+ }
210+
211+ fn json ( & self ) -> Value {
212+ // This message is only helpful in human mode, we don't need it in JSON mode.
213+ Value :: Null
214+ }
215+ }
216+
217+ fn generate_constructor_placeholder_arguments ( constructor : AbiConstructor ) -> String {
218+ constructor
219+ . inputs
220+ . into_iter ( )
221+ . map ( |input| {
222+ let input_type = input
223+ . r#type
224+ . split ( "::" )
225+ . last ( )
226+ . expect ( "Failed to get last part of input type" ) ;
227+ format ! ( "<{}: {})>" , input. name, input_type)
228+ } )
229+ . collect :: < Vec < String > > ( )
230+ . join ( ", " )
231+ }
232+
233+ fn generate_arguments_flag ( abi : & [ AbiEntry ] ) -> Option < String > {
234+ let arguments = abi. iter ( ) . find_map ( |entry| {
235+ if let AbiEntry :: Constructor ( constructor) = entry {
236+ let arguments = generate_constructor_placeholder_arguments ( constructor. clone ( ) ) ;
237+ ( !arguments. is_empty ( ) ) . then_some ( arguments)
238+ } else {
239+ None
240+ }
241+ } ) ;
242+
243+ arguments. map ( |arguments| format ! ( "--arguments '{arguments}'" ) )
244+ }
245+
246+ fn generate_accounts_file_flag ( accounts_file : Option < & String > ) -> Option < String > {
247+ accounts_file
248+ . as_ref ( )
249+ . map ( |file| format ! ( "--accounts-file {file}" ) )
250+ }
0 commit comments