1use super::{
18 pallet_contracts_primitives::ContractExecResult,
19 state_call,
20 submit_extrinsic,
21 ContractMessageTranscoder,
22 ErrorVariant,
23};
24use crate::{
25 check_env_types,
26 extrinsic_calls::Call,
27 extrinsic_opts::ExtrinsicOpts,
28};
29
30use anyhow::{
31 anyhow,
32 Result,
33};
34use ink_env::Environment;
35use scale::Encode;
36use sp_weights::Weight;
37
38use subxt::{
39 backend::{
40 legacy::LegacyRpcMethods,
41 rpc::RpcClient,
42 },
43 blocks::ExtrinsicEvents,
44 config::{
45 DefaultExtrinsicParams,
46 ExtrinsicParams,
47 },
48 ext::{
49 scale_decode::IntoVisitor,
50 scale_encode::EncodeAsType,
51 },
52 tx,
53 Config,
54 OnlineClient,
55};
56
57pub struct CallCommandBuilder<C: Config, E: Environment, Signer: Clone> {
59 contract: C::AccountId,
60 message: String,
61 args: Vec<String>,
62 extrinsic_opts: ExtrinsicOpts<C, E, Signer>,
63 gas_limit: Option<u64>,
64 proof_size: Option<u64>,
65 value: E::Balance,
66}
67
68impl<C: Config, E: Environment, Signer> CallCommandBuilder<C, E, Signer>
69where
70 E::Balance: Default,
71 Signer: tx::Signer<C> + Clone,
72{
73 pub fn new(
75 contract: C::AccountId,
76 message: &str,
77 extrinsic_opts: ExtrinsicOpts<C, E, Signer>,
78 ) -> CallCommandBuilder<C, E, Signer> {
79 CallCommandBuilder {
80 contract,
81 message: message.to_string(),
82 args: Vec::new(),
83 extrinsic_opts,
84 gas_limit: None,
85 proof_size: None,
86 value: Default::default(),
87 }
88 }
89
90 pub fn args<T: ToString>(self, args: Vec<T>) -> Self {
92 let mut this = self;
93 this.args = args.into_iter().map(|arg| arg.to_string()).collect();
94 this
95 }
96
97 pub fn gas_limit(self, gas_limit: Option<u64>) -> Self {
99 let mut this = self;
100 this.gas_limit = gas_limit;
101 this
102 }
103
104 pub fn proof_size(self, proof_size: Option<u64>) -> Self {
106 let mut this = self;
107 this.proof_size = proof_size;
108 this
109 }
110
111 pub fn value(self, value: E::Balance) -> Self {
113 let mut this = self;
114 this.value = value;
115 this
116 }
117
118 pub async fn done(self) -> Result<CallExec<C, E, Signer>> {
128 let artifacts = self.extrinsic_opts.contract_artifacts()?;
129 let transcoder = artifacts.contract_transcoder()?;
130
131 let call_data = transcoder.encode(&self.message, &self.args)?;
132 tracing::debug!("Message data: {:?}", hex::encode(&call_data));
133
134 let url = self.extrinsic_opts.url();
135 let rpc = RpcClient::from_url(&url).await?;
136 let client = OnlineClient::from_rpc_client(rpc.clone()).await?;
137 let rpc = LegacyRpcMethods::new(rpc);
138 check_env_types(&client, &transcoder, self.extrinsic_opts.verbosity())?;
139
140 Ok(CallExec {
141 contract: self.contract,
142 message: self.message.clone(),
143 args: self.args.clone(),
144 opts: self.extrinsic_opts,
145 gas_limit: self.gas_limit,
146 proof_size: self.proof_size,
147 value: self.value,
148 rpc,
149 client,
150 transcoder,
151 call_data,
152 })
153 }
154}
155
156pub struct CallExec<C: Config, E: Environment, Signer: Clone> {
157 contract: C::AccountId,
158 message: String,
159 args: Vec<String>,
160 opts: ExtrinsicOpts<C, E, Signer>,
161 gas_limit: Option<u64>,
162 proof_size: Option<u64>,
163 value: E::Balance,
164 rpc: LegacyRpcMethods<C>,
165 client: OnlineClient<C>,
166 transcoder: ContractMessageTranscoder,
167 call_data: Vec<u8>,
168}
169
170impl<C: Config, E: Environment, Signer> CallExec<C, E, Signer>
171where
172 <C::ExtrinsicParams as ExtrinsicParams<C>>::Params:
173 From<<DefaultExtrinsicParams<C> as ExtrinsicParams<C>>::Params>,
174 C::AccountId: EncodeAsType + IntoVisitor,
175 E::Balance: EncodeAsType,
176 Signer: tx::Signer<C> + Clone,
177{
178 pub async fn call_dry_run(&self) -> Result<ContractExecResult<E::Balance>> {
188 let storage_deposit_limit = self.opts.storage_deposit_limit();
189 let call_request = CallRequest {
190 origin: self.opts.signer().account_id(),
191 dest: self.contract.clone(),
192 value: self.value,
193 gas_limit: None,
194 storage_deposit_limit,
195 input_data: self.call_data.clone(),
196 };
197 state_call(&self.rpc, "ContractsApi_call", call_request).await
198 }
199
200 pub async fn call(
209 &self,
210 gas_limit: Option<Weight>,
211 ) -> Result<ExtrinsicEvents<C>, ErrorVariant> {
212 if !self
213 .transcoder()
214 .metadata()
215 .spec()
216 .messages()
217 .iter()
218 .find(|msg| msg.label() == &self.message)
219 .expect("message exist after calling CallExec::done()")
220 .mutates()
221 {
222 let inner = anyhow!(
223 "Tried to execute a call on the immutable contract message '{}'. Please do a dry-run instead.",
224 &self.message
225 );
226 return Err(inner.into())
227 }
228
229 let gas_limit = match gas_limit {
231 Some(gas_limit) => gas_limit,
232 None => self.estimate_gas().await?,
233 };
234 tracing::debug!("calling contract {:?}", self.contract);
235 let storage_deposit_limit = self.opts.storage_deposit_limit();
236
237 let call = Call::new(
238 self.contract.clone().into(),
239 self.value,
240 gas_limit,
241 storage_deposit_limit,
242 self.call_data.clone(),
243 )
244 .build();
245
246 let result =
247 submit_extrinsic(&self.client, &self.rpc, &call, self.opts.signer()).await?;
248
249 Ok(result)
250 }
251
252 pub async fn estimate_gas(&self) -> Result<Weight> {
261 match (self.gas_limit, self.proof_size) {
262 (Some(ref_time), Some(proof_size)) => {
263 Ok(Weight::from_parts(ref_time, proof_size))
264 }
265 _ => {
266 let call_result = self.call_dry_run().await?;
267 match call_result.result {
268 Ok(_) => {
269 let ref_time = self
272 .gas_limit
273 .unwrap_or_else(|| call_result.gas_required.ref_time());
274 let proof_size = self
275 .proof_size
276 .unwrap_or_else(|| call_result.gas_required.proof_size());
277 Ok(Weight::from_parts(ref_time, proof_size))
278 }
279 Err(ref err) => {
280 let object = ErrorVariant::from_dispatch_error(
281 err,
282 &self.client.metadata(),
283 )?;
284 Err(anyhow!("Pre-submission dry-run failed. Error: {}", object))
285 }
286 }
287 }
288 }
289 }
290
291 pub fn contract(&self) -> &C::AccountId {
293 &self.contract
294 }
295
296 pub fn message(&self) -> &str {
298 &self.message
299 }
300
301 pub fn args(&self) -> &Vec<String> {
303 &self.args
304 }
305
306 pub fn opts(&self) -> &ExtrinsicOpts<C, E, Signer> {
308 &self.opts
309 }
310
311 pub fn gas_limit(&self) -> Option<u64> {
313 self.gas_limit
314 }
315
316 pub fn proof_size(&self) -> Option<u64> {
318 self.proof_size
319 }
320
321 pub fn value(&self) -> &E::Balance {
323 &self.value
324 }
325
326 pub fn client(&self) -> &OnlineClient<C> {
328 &self.client
329 }
330
331 pub fn transcoder(&self) -> &ContractMessageTranscoder {
333 &self.transcoder
334 }
335
336 pub fn call_data(&self) -> &Vec<u8> {
338 &self.call_data
339 }
340}
341
342#[derive(Encode)]
346struct CallRequest<AccountId, Balance> {
347 origin: AccountId,
348 dest: AccountId,
349 value: Balance,
350 gas_limit: Option<Weight>,
351 storage_deposit_limit: Option<Balance>,
352 input_data: Vec<u8>,
353}