1use super::{
18 events::{
19 CodeStored,
20 ContractInstantiated,
21 },
22 pallet_contracts_primitives::{
23 ContractInstantiateResult,
24 StorageDeposit,
25 },
26 state_call,
27 submit_extrinsic,
28 ContractMessageTranscoder,
29 ErrorVariant,
30};
31use crate::{
32 check_env_types,
33 extrinsic_calls::{
34 Instantiate,
35 InstantiateWithCode,
36 },
37 extrinsic_opts::ExtrinsicOpts,
38};
39use anyhow::{
40 anyhow,
41 Context,
42 Result,
43};
44use contract_transcode::Value;
45use ink_env::Environment;
46use serde::Serialize;
47
48use scale::{
49 Decode,
50 Encode,
51};
52use sp_core::Bytes;
53use sp_weights::Weight;
54use std::fmt::Display;
55use subxt::{
56 backend::{
57 legacy::LegacyRpcMethods,
58 rpc::RpcClient,
59 },
60 blocks::ExtrinsicEvents,
61 config::{
62 DefaultExtrinsicParams,
63 ExtrinsicParams,
64 },
65 ext::{
66 scale_decode::IntoVisitor,
67 scale_encode::EncodeAsType,
68 },
69 tx,
70 Config,
71 OnlineClient,
72};
73
74pub struct InstantiateCommandBuilder<C: Config, E: Environment, Signer: Clone> {
76 constructor: String,
77 args: Vec<String>,
78 extrinsic_opts: ExtrinsicOpts<C, E, Signer>,
79 value: E::Balance,
80 gas_limit: Option<u64>,
81 proof_size: Option<u64>,
82 salt: Option<Bytes>,
83}
84
85impl<C: Config, E: Environment, Signer> InstantiateCommandBuilder<C, E, Signer>
86where
87 E::Balance: Default,
88 Signer: tx::Signer<C> + Clone,
89 C::Hash: From<[u8; 32]>,
90{
91 pub fn new(
93 extrinsic_opts: ExtrinsicOpts<C, E, Signer>,
94 ) -> InstantiateCommandBuilder<C, E, Signer> {
95 InstantiateCommandBuilder {
96 constructor: String::from("new"),
97 args: Vec::new(),
98 extrinsic_opts,
99 value: Default::default(),
100 gas_limit: None,
101 proof_size: None,
102 salt: None,
103 }
104 }
105
106 pub fn constructor<T: Into<String>>(self, constructor: T) -> Self {
108 let mut this = self;
109 this.constructor = constructor.into();
110 this
111 }
112
113 pub fn args<T: ToString>(self, args: Vec<T>) -> Self {
115 let mut this = self;
116 this.args = args.into_iter().map(|arg| arg.to_string()).collect();
117 this
118 }
119
120 pub fn value(self, value: E::Balance) -> Self {
122 let mut this = self;
123 this.value = value;
124 this
125 }
126
127 pub fn gas_limit(self, gas_limit: Option<u64>) -> Self {
129 let mut this = self;
130 this.gas_limit = gas_limit;
131 this
132 }
133
134 pub fn proof_size(self, proof_size: Option<u64>) -> Self {
136 let mut this = self;
137 this.proof_size = proof_size;
138 this
139 }
140
141 pub fn salt(self, salt: Option<Bytes>) -> Self {
143 let mut this = self;
144 this.salt = salt;
145 this
146 }
147
148 pub async fn done(self) -> Result<InstantiateExec<C, E, Signer>> {
158 let artifacts = self.extrinsic_opts.contract_artifacts()?;
159 let transcoder = artifacts.contract_transcoder()?;
160 let data = transcoder.encode(&self.constructor, &self.args)?;
161 let url = self.extrinsic_opts.url();
162 let code = if let Some(code) = artifacts.code {
163 Code::Upload(code.0)
164 } else {
165 let code_hash = artifacts.code_hash()?;
166 Code::Existing(code_hash.into())
167 };
168 let salt = self.salt.clone().map(|s| s.0).unwrap_or_default();
169
170 let rpc_cli = RpcClient::from_url(&url).await?;
171 let client = OnlineClient::from_rpc_client(rpc_cli.clone()).await?;
172 check_env_types(&client, &transcoder, self.extrinsic_opts.verbosity())?;
173 let rpc = LegacyRpcMethods::new(rpc_cli);
174
175 let args = InstantiateArgs {
176 constructor: self.constructor.clone(),
177 raw_args: self.args.clone(),
178 value: self.value,
179 gas_limit: self.gas_limit,
180 proof_size: self.proof_size,
181 storage_deposit_limit: self.extrinsic_opts.storage_deposit_limit(),
182 code,
183 data,
184 salt,
185 };
186
187 Ok(InstantiateExec {
188 args,
189 opts: self.extrinsic_opts,
190 rpc,
191 client,
192 transcoder,
193 })
194 }
195}
196
197pub struct InstantiateArgs<C: Config, E: Environment> {
198 constructor: String,
199 raw_args: Vec<String>,
200 value: E::Balance,
201 gas_limit: Option<u64>,
202 proof_size: Option<u64>,
203 storage_deposit_limit: Option<E::Balance>,
204 code: Code<C::Hash>,
205 data: Vec<u8>,
206 salt: Vec<u8>,
207}
208
209impl<C: Config, E: Environment> InstantiateArgs<C, E> {
210 pub fn constructor(&self) -> &str {
212 &self.constructor
213 }
214
215 pub fn raw_args(&self) -> &[String] {
217 &self.raw_args
218 }
219
220 pub fn value(&self) -> E::Balance {
222 self.value
223 }
224
225 pub fn gas_limit(&self) -> Option<u64> {
227 self.gas_limit
228 }
229
230 pub fn proof_size(&self) -> Option<u64> {
232 self.proof_size
233 }
234
235 pub fn storage_deposit_limit_compact(&self) -> Option<scale::Compact<E::Balance>> {
237 self.storage_deposit_limit.map(Into::into)
238 }
239
240 pub fn code(&self) -> &Code<C::Hash> {
241 &self.code
242 }
243
244 pub fn data(&self) -> &[u8] {
246 &self.data
247 }
248
249 pub fn salt(&self) -> &[u8] {
251 &self.salt
252 }
253}
254
255pub struct InstantiateExec<C: Config, E: Environment, Signer: Clone> {
256 opts: ExtrinsicOpts<C, E, Signer>,
257 args: InstantiateArgs<C, E>,
258 rpc: LegacyRpcMethods<C>,
259 client: OnlineClient<C>,
260 transcoder: ContractMessageTranscoder,
261}
262
263impl<C: Config, E: Environment, Signer> InstantiateExec<C, E, Signer>
264where
265 C::AccountId: Decode,
266 <C::ExtrinsicParams as ExtrinsicParams<C>>::Params:
267 From<<DefaultExtrinsicParams<C> as ExtrinsicParams<C>>::Params>,
268 C::Hash: IntoVisitor + EncodeAsType,
269 C::AccountId: IntoVisitor + Display,
270 E::Balance: Serialize + EncodeAsType,
271 Signer: tx::Signer<C> + Clone,
272{
273 pub async fn decode_instantiate_dry_run(
282 &self,
283 result: &ContractInstantiateResult<C::AccountId, E::Balance>,
284 ) -> Result<InstantiateDryRunResult<E::Balance>, ErrorVariant> {
285 tracing::debug!("instantiate data {:?}", self.args.data);
286 match result.result {
287 Ok(ref ret_val) => {
288 let value = self
289 .transcoder
290 .decode_constructor_return(
291 &self.args.constructor,
292 &mut &ret_val.result.data[..],
293 )
294 .context(format!("Failed to decode return value {:?}", &ret_val))?;
295 let dry_run_result = InstantiateDryRunResult {
296 result: value,
297 contract: ret_val.account_id.to_string(),
298 reverted: ret_val.result.did_revert(),
299 gas_consumed: result.gas_consumed,
300 gas_required: result.gas_required,
301 storage_deposit: result.storage_deposit.clone(),
302 };
303 Ok(dry_run_result)
304 }
305 Err(ref err) => {
306 let metadata = self.client.metadata();
307 Err(ErrorVariant::from_dispatch_error(err, &metadata)?)
308 }
309 }
310 }
311
312 pub async fn instantiate_dry_run(
321 &self,
322 ) -> Result<ContractInstantiateResult<C::AccountId, E::Balance>> {
323 let storage_deposit_limit = self.args.storage_deposit_limit;
324 let call_request = InstantiateRequest::<C, E> {
325 origin: self.opts.signer().account_id(),
326 value: self.args.value,
327 gas_limit: None,
328 storage_deposit_limit,
329 code: self.args.code.clone(),
330 data: self.args.data.clone(),
331 salt: self.args.salt.clone(),
332 };
333 state_call(&self.rpc, "ContractsApi_instantiate", &call_request).await
334 }
335
336 async fn instantiate_with_code(
337 &self,
338 code: Vec<u8>,
339 gas_limit: Weight,
340 ) -> Result<InstantiateExecResult<C>, ErrorVariant> {
341 let call = InstantiateWithCode::new(
342 self.args.value,
343 gas_limit,
344 self.args.storage_deposit_limit,
345 code,
346 self.args.data.clone(),
347 self.args.salt.clone(),
348 )
349 .build();
350
351 let events =
352 submit_extrinsic(&self.client, &self.rpc, &call, self.opts.signer()).await?;
353
354 let code_hash = events
357 .find_first::<CodeStored<C::Hash>>()?
358 .map(|code_stored| code_stored.code_hash);
359
360 let instantiated = events
361 .find_last::<ContractInstantiated<C::AccountId>>()?
362 .ok_or_else(|| anyhow!("Failed to find Instantiated event"))?;
363
364 Ok(InstantiateExecResult {
365 events,
366 code_hash,
367 contract_address: instantiated.contract,
368 })
369 }
370
371 async fn instantiate_with_code_hash(
372 &self,
373 code_hash: C::Hash,
374 gas_limit: Weight,
375 ) -> Result<InstantiateExecResult<C>, ErrorVariant> {
376 let call = Instantiate::<C::Hash, E::Balance>::new(
377 self.args.value,
378 gas_limit,
379 self.args.storage_deposit_limit,
380 code_hash,
381 self.args.data.clone(),
382 self.args.salt.clone(),
383 )
384 .build();
385
386 let events =
387 submit_extrinsic(&self.client, &self.rpc, &call, self.opts.signer()).await?;
388
389 let instantiated = events
390 .find_first::<ContractInstantiated<C::AccountId>>()?
391 .ok_or_else(|| anyhow!("Failed to find Instantiated event"))?;
392
393 Ok(InstantiateExecResult {
394 events,
395 code_hash: None,
396 contract_address: instantiated.contract,
397 })
398 }
399
400 pub async fn instantiate(
411 &self,
412 gas_limit: Option<Weight>,
413 ) -> Result<InstantiateExecResult<C>, ErrorVariant> {
414 let gas_limit = match gas_limit {
416 Some(gas_limit) => gas_limit,
417 None => self.estimate_gas().await?,
418 };
419 match self.args.code.clone() {
420 Code::Upload(code) => self.instantiate_with_code(code, gas_limit).await,
421 Code::Existing(code_hash) => {
422 self.instantiate_with_code_hash(code_hash, gas_limit).await
423 }
424 }
425 }
426
427 pub async fn estimate_gas(&self) -> Result<Weight> {
436 match (self.args.gas_limit, self.args.proof_size) {
437 (Some(ref_time), Some(proof_size)) => {
438 Ok(Weight::from_parts(ref_time, proof_size))
439 }
440 _ => {
441 let instantiate_result = self.instantiate_dry_run().await?;
442 match instantiate_result.result {
443 Ok(_) => {
444 let ref_time = self.args.gas_limit.unwrap_or_else(|| {
447 instantiate_result.gas_required.ref_time()
448 });
449 let proof_size = self.args.proof_size.unwrap_or_else(|| {
450 instantiate_result.gas_required.proof_size()
451 });
452 Ok(Weight::from_parts(ref_time, proof_size))
453 }
454 Err(ref err) => {
455 let object = ErrorVariant::from_dispatch_error(
456 err,
457 &self.client.metadata(),
458 )?;
459 Err(anyhow!("Pre-submission dry-run failed. Error: {}", object))
460 }
461 }
462 }
463 }
464 }
465
466 pub fn opts(&self) -> &ExtrinsicOpts<C, E, Signer> {
468 &self.opts
469 }
470
471 pub fn args(&self) -> &InstantiateArgs<C, E> {
473 &self.args
474 }
475
476 pub fn client(&self) -> &OnlineClient<C> {
478 &self.client
479 }
480
481 pub fn transcoder(&self) -> &ContractMessageTranscoder {
483 &self.transcoder
484 }
485}
486
487pub struct InstantiateExecResult<C: Config> {
489 pub events: ExtrinsicEvents<C>,
490 pub code_hash: Option<C::Hash>,
491 pub contract_address: C::AccountId,
492}
493
494#[derive(serde::Serialize)]
496pub struct InstantiateDryRunResult<Balance: Serialize> {
497 pub result: Value,
499 pub contract: String,
501 pub reverted: bool,
503 pub gas_consumed: Weight,
504 pub gas_required: Weight,
505 pub storage_deposit: StorageDeposit<Balance>,
507}
508
509impl<Balance: Serialize> InstantiateDryRunResult<Balance> {
510 pub fn to_json(&self) -> Result<String> {
512 Ok(serde_json::to_string_pretty(self)?)
513 }
514}
515
516#[derive(Encode)]
518struct InstantiateRequest<C: Config, E: Environment> {
519 origin: C::AccountId,
520 value: E::Balance,
521 gas_limit: Option<Weight>,
522 storage_deposit_limit: Option<E::Balance>,
523 code: Code<C::Hash>,
524 data: Vec<u8>,
525 salt: Vec<u8>,
526}
527
528#[derive(Clone, Encode)]
530pub enum Code<Hash>
531where
532 Hash: Clone,
533{
534 Upload(Vec<u8>),
536 Existing(Hash),
538}