1
1
use super :: { command:: CommandResponse , explorer_link:: OutputLink } ;
2
2
use crate :: helpers:: block_explorer:: LinkProvider ;
3
3
use crate :: response:: cast_message:: SncastMessage ;
4
+ use anyhow:: Error ;
5
+ use camino:: Utf8PathBuf ;
4
6
use conversions:: string:: IntoHexStr ;
5
7
use conversions:: { padded_felt:: PaddedFelt , serde:: serialize:: CairoSerialize } ;
6
8
use foundry_ui:: Message ;
@@ -9,6 +11,8 @@ use indoc::formatdoc;
9
11
use serde:: { Deserialize , Serialize } ;
10
12
use serde_json:: Value ;
11
13
use serde_json:: json;
14
+ use starknet:: core:: types:: contract:: { AbiConstructor , AbiEntry } ;
15
+ use std:: fmt:: Write ;
12
16
#[ derive( Clone , Serialize , Deserialize , CairoSerialize , Debug , PartialEq ) ]
13
17
pub struct DeclareTransactionResponse {
14
18
pub class_hash : PaddedFelt ,
@@ -129,3 +133,118 @@ impl OutputLink for DeclareTransactionResponse {
129
133
)
130
134
}
131
135
}
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