contract_extrinsics/
instantiate.rs

1// Copyright (C) Use Ink (UK) Ltd.
2// This file is part of cargo-contract.
3//
4// cargo-contract is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// cargo-contract is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with cargo-contract.  If not, see <http://www.gnu.org/licenses/>.
16
17use 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
74/// A builder for the instantiate command.
75pub 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    /// Returns a clean builder for [`InstantiateExec`].
92    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    /// Sets the name of the contract constructor to call.
107    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    /// Sets the constructor arguments.
114    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    /// Sets the initial balance to transfer to the instantiated contract.
121    pub fn value(self, value: E::Balance) -> Self {
122        let mut this = self;
123        this.value = value;
124        this
125    }
126
127    /// Sets the maximum amount of gas to be used for this command.
128    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    /// Sets the maximum proof size for this instantiation.
135    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    /// Sets the salt used in the address derivation of the new contract.
142    pub fn salt(self, salt: Option<Bytes>) -> Self {
143        let mut this = self;
144        this.salt = salt;
145        this
146    }
147
148    /// Preprocesses contract artifacts and options for instantiation.
149    ///
150    /// This function prepares the required data for instantiating a contract based on the
151    /// provided contract artifacts and options. It ensures that the necessary contract
152    /// code is available, sets up the client, signer, and other relevant parameters,
153    /// preparing for the instantiation process.
154    ///
155    /// Returns the [`InstantiateExec`] containing the preprocessed data for the
156    /// instantiation, or an error in case of failure.
157    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    /// Returns the constructor name.
211    pub fn constructor(&self) -> &str {
212        &self.constructor
213    }
214
215    /// Returns the constructor raw arguments.
216    pub fn raw_args(&self) -> &[String] {
217        &self.raw_args
218    }
219
220    /// Returns the value to transfer to the instantiated contract.
221    pub fn value(&self) -> E::Balance {
222        self.value
223    }
224
225    /// Returns the maximum amount of gas to be used for this command.
226    pub fn gas_limit(&self) -> Option<u64> {
227        self.gas_limit
228    }
229
230    /// Returns the maximum proof size for this instantiation.
231    pub fn proof_size(&self) -> Option<u64> {
232        self.proof_size
233    }
234
235    /// Returns the storage deposit limit for this instantiation.
236    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    /// Returns the input data for the contract constructor.
245    pub fn data(&self) -> &[u8] {
246        &self.data
247    }
248
249    /// Returns the salt used in the address derivation of the new contract.
250    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    /// Decodes the result of a simulated contract instantiation.
274    ///
275    /// This function decodes the result of a simulated contract instantiation dry run.
276    /// It processes the returned data, including the constructor's return value, contract
277    /// address, gas consumption, and storage deposit, and packages them into an
278    /// [`InstantiateDryRunResult`].
279    ///
280    /// Returns the decoded dry run result, or an error in case of failure.
281    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    /// Simulates a contract instantiation without modifying the blockchain.
313    ///
314    /// This function performs a dry run simulation of a contract instantiation, capturing
315    /// essential information such as the contract address, gas consumption, and storage
316    /// deposit. The simulation is executed without actually executing the
317    /// instantiation on the blockchain.
318    ///
319    /// Returns the dry run simulation result, or an error in case of failure.
320    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        // The CodeStored event is only raised if the contract has not already been
355        // uploaded.
356        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    /// Initiates the deployment of a smart contract on the blockchain.
401    ///
402    /// This function can be used to deploy a contract using either its source code or an
403    /// existing code hash. It triggers the instantiation process by submitting an
404    /// extrinsic with the specified gas limit, storage deposit, code or code hash,
405    /// input data, and salt.
406    ///
407    /// The deployment result provides essential information about the instantiation,
408    /// encapsulated in an [`InstantiateExecResult`] object, including the contract's
409    /// result, contract address, and token metadata.
410    pub async fn instantiate(
411        &self,
412        gas_limit: Option<Weight>,
413    ) -> Result<InstantiateExecResult<C>, ErrorVariant> {
414        // use user specified values where provided, otherwise estimate
415        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    /// Estimates the gas required for the contract instantiation process without
428    /// modifying the blockchain.
429    ///
430    /// This function provides a gas estimation for contract instantiation, considering
431    /// the user-specified values or using estimates based on a dry run.
432    ///
433    /// Returns the estimated gas weight of type [`Weight`] for contract instantiation, or
434    /// an error.
435    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                        // use user specified values where provided, otherwise use the
445                        // estimates
446                        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    /// Returns the extrinsic options.
467    pub fn opts(&self) -> &ExtrinsicOpts<C, E, Signer> {
468        &self.opts
469    }
470
471    /// Returns the instantiate arguments.
472    pub fn args(&self) -> &InstantiateArgs<C, E> {
473        &self.args
474    }
475
476    /// Returns the client.
477    pub fn client(&self) -> &OnlineClient<C> {
478        &self.client
479    }
480
481    /// Returns the contract message transcoder.
482    pub fn transcoder(&self) -> &ContractMessageTranscoder {
483        &self.transcoder
484    }
485}
486
487/// A struct representing the result of an instantiate command execution.
488pub 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/// Result of the contract call
495#[derive(serde::Serialize)]
496pub struct InstantiateDryRunResult<Balance: Serialize> {
497    /// The decoded result returned from the constructor
498    pub result: Value,
499    /// contract address
500    pub contract: String,
501    /// Was the operation reverted
502    pub reverted: bool,
503    pub gas_consumed: Weight,
504    pub gas_required: Weight,
505    /// Storage deposit after the operation
506    pub storage_deposit: StorageDeposit<Balance>,
507}
508
509impl<Balance: Serialize> InstantiateDryRunResult<Balance> {
510    /// Returns a result in json format
511    pub fn to_json(&self) -> Result<String> {
512        Ok(serde_json::to_string_pretty(self)?)
513    }
514}
515
516/// A struct that encodes RPC parameters required to instantiate a new smart contract.
517#[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/// Reference to an existing code hash or a new Wasm module.
529#[derive(Clone, Encode)]
530pub enum Code<Hash>
531where
532    Hash: Clone,
533{
534    /// A Wasm module as raw bytes.
535    Upload(Vec<u8>),
536    /// The code hash of an on-chain Wasm blob.
537    Existing(Hash),
538}