gclient/api/
calls.rs

1// This file is part of Gear.
2
3// Copyright (C) 2022-2025 Gear Technologies Inc.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19use super::{GearApi, Result};
20use crate::{Error, api::storage::account_id::IntoAccountId32, utils};
21use anyhow::anyhow;
22use gear_core::{gas::LockId, ids::*, memory::PageBuf, pages::GearPage};
23use gear_utils::{MemoryPageDump, ProgramMemoryDump};
24use gsdk::{
25    AsGear, Error as GsdkError, GearGasNode, GearGasNodeId, IntoSubstrate, IntoSubxt,
26    config::GearConfig,
27    ext::{
28        sp_runtime::{AccountId32, MultiAddress},
29        subxt::{blocks::ExtrinsicEvents, utils::H256},
30    },
31    gear::{
32        Event,
33        balances::Event as BalancesEvent,
34        gear::Event as GearEvent,
35        gear_eth_bridge::Event as GearEthBridgeEvent,
36        runtime_types::{
37            frame_system::pallet::Call as SystemCall,
38            gear_common::event::{CodeChangeKind, MessageEntry},
39            gear_core::program::ActiveProgram,
40            pallet_balances::{pallet::Call as BalancesCall, types::AccountData},
41            pallet_gear::pallet::Call as GearCall,
42            pallet_gear_bank::pallet::BankAccount,
43            pallet_gear_voucher::internal::VoucherId,
44            sp_weights::weight_v2::Weight,
45            vara_runtime::RuntimeCall,
46        },
47        system::Event as SystemEvent,
48        utility::Event as UtilityEvent,
49    },
50};
51use hex::ToHex;
52use parity_scale_codec::{Decode, Encode};
53use std::{
54    collections::{BTreeMap, HashMap, HashSet},
55    path::Path,
56};
57
58impl GearApi {
59    /// Sends the pallet-gear-eth-bridge::reset_overflowed_queue extrinsic.
60    ///
61    /// This function returns a hash of the block with the transaction.
62    pub async fn reset_overflowed_queue(&self, encoded_finality_proof: Vec<u8>) -> Result<H256> {
63        let tx = self
64            .0
65            .calls()
66            .reset_overflowed_queue(encoded_finality_proof)
67            .await?;
68
69        for event in tx.wait_for_success().await?.iter() {
70            if let Event::GearEthBridge(GearEthBridgeEvent::QueueReset) = event?.as_gear()? {
71                return Ok(tx.block_hash());
72            }
73        }
74
75        Err(Error::EventNotFound)
76    }
77
78    /// Returns original wasm code for the given `code_id` at specified
79    /// `at_block_hash`.
80    pub async fn original_code_at(
81        &self,
82        code_id: CodeId,
83        at_block_hash: Option<H256>,
84    ) -> Result<Vec<u8>> {
85        self.0
86            .api()
87            .original_code_storage_at(code_id, at_block_hash)
88            .await
89            .map_err(Into::into)
90    }
91
92    /// Returns `ActiveProgram` for the given `program_id` at specified
93    /// `at_block_hash`.
94    pub async fn program_at(
95        &self,
96        program_id: ActorId,
97        at_block_hash: Option<H256>,
98    ) -> Result<ActiveProgram<u32>> {
99        self.0
100            .api()
101            .gprog_at(program_id, at_block_hash)
102            .await
103            .map_err(Into::into)
104    }
105
106    /// Transfer `value` to `destination`'s account.
107    ///
108    /// Sends the
109    /// [`pallet_balances::transfer`](https://crates.parity.io/pallet_balances/pallet/struct.Pallet.html#method.transfer)
110    /// extrinsic.
111    ///
112    /// This function returns a hash of the block with the transfer transaction.
113    pub async fn transfer_keep_alive(&self, destination: ActorId, value: u128) -> Result<H256> {
114        let destination: [u8; 32] = destination.into();
115
116        let tx = self
117            .0
118            .calls()
119            .transfer_keep_alive(destination, value)
120            .await?;
121
122        for event in tx.wait_for_success().await?.iter() {
123            if let Event::Balances(BalancesEvent::Transfer { .. }) = event?.as_gear()? {
124                return Ok(tx.block_hash());
125            }
126        }
127
128        // Sending zero value is a no-op, so now event occurs.
129        if value == 0 {
130            return Ok(tx.block_hash());
131        }
132
133        Err(Error::EventNotFound)
134    }
135
136    /// Transfer `value` to `destination`'s account.
137    ///
138    /// Sends the
139    /// [`pallet_balances::transfer`](https://crates.parity.io/pallet_balances/pallet/struct.Pallet.html#method.transfer)
140    /// extrinsic.
141    ///
142    /// This function returns a hash of the block with the transfer transaction.
143    pub async fn transfer_allow_death(&self, destination: ActorId, value: u128) -> Result<H256> {
144        let destination: [u8; 32] = destination.into();
145
146        let tx = self
147            .0
148            .calls()
149            .transfer_allow_death(destination, value)
150            .await?;
151
152        for event in tx.wait_for_success().await?.iter() {
153            if let Event::Balances(BalancesEvent::Transfer { .. }) = event?.as_gear()? {
154                return Ok(tx.block_hash());
155            }
156        }
157
158        // Sending zero value is a no-op, so now event occurs.
159        if value == 0 {
160            return Ok(tx.block_hash());
161        }
162
163        Err(Error::EventNotFound)
164    }
165
166    /// Transfer `value` to `destination`'s account.
167    ///
168    /// Sends the
169    /// [`pallet_balances::transfer`](https://crates.parity.io/pallet_balances/pallet/struct.Pallet.html#method.transfer)
170    /// extrinsic.
171    ///
172    /// This function returns a hash of the block with the transfer transaction.
173    pub async fn transfer_all(&self, destination: ActorId, keep_alive: bool) -> Result<H256> {
174        let destination: [u8; 32] = destination.into();
175
176        let tx = self.0.calls().transfer_all(destination, keep_alive).await?;
177
178        for event in tx.wait_for_success().await?.iter() {
179            if let Event::Balances(BalancesEvent::Transfer { .. }) = event?.as_gear()? {
180                return Ok(tx.block_hash());
181            }
182        }
183
184        Err(Error::EventNotFound)
185    }
186
187    /// Create a new program from a previously uploaded code identified by
188    /// [`CodeId`](https://docs.rs/gear_core/ids/struct.CodeId.html) and
189    /// initialize it with a byte slice `payload`.
190    ///
191    /// Sends the
192    /// [`pallet_gear::create_program`](https://docs.rs/pallet_gear/pallet/struct.Pallet.html#method.create_program)
193    /// extrinsic.
194    ///
195    /// Parameters:
196    ///
197    /// - `code_id` is the code identifier that can be obtained by calling the
198    ///   [`upload_code`](Self::upload_code) function;
199    /// - `salt` is the arbitrary data needed to generate an address for a new
200    ///   program (control of salt uniqueness is entirely on the function
201    ///   caller’s side);
202    /// - `payload` vector contains data to be processed in the `init` function
203    ///   of the newly deployed "child" program;
204    /// - `gas_limit` is the maximum gas amount allowed to spend for the program
205    ///   creation and initialization;
206    /// - `value` to be transferred to the program's account during
207    ///   initialization.
208    ///
209    /// This function returns a tuple with an init message identifier, newly
210    /// created program identifier, and a hash of the block with the message
211    /// enqueuing transaction.
212    ///
213    /// # See also
214    ///
215    /// - [`create_program`](Self::create_program) function initializes a newly
216    ///   created program with an encoded payload.
217    /// - [`create_program_bytes_batch`](Self::create_program_bytes_batch)
218    ///   function creates a batch of programs and initializes them.
219    /// - [`upload_code`](Self::upload_code) function uploads a code and returns
220    ///   its identifier.
221    /// - [`upload_program_bytes`](Self::upload_program_bytes) function uploads
222    ///   a new program and initialize it.
223    pub async fn create_program_bytes(
224        &self,
225        code_id: CodeId,
226        salt: impl AsRef<[u8]>,
227        payload: impl AsRef<[u8]>,
228        gas_limit: u64,
229        value: u128,
230    ) -> Result<(MessageId, ActorId, H256)> {
231        let salt = salt.as_ref().to_vec();
232        let payload = payload.as_ref().to_vec();
233
234        let tx = self
235            .0
236            .calls()
237            .create_program(code_id, salt, payload, gas_limit, value)
238            .await?;
239
240        for event in tx.wait_for_success().await?.iter() {
241            if let Event::Gear(GearEvent::MessageQueued {
242                id,
243                destination,
244                entry: MessageEntry::Init,
245                ..
246            }) = event?.as_gear()?
247            {
248                return Ok((id, destination, tx.block_hash()));
249            }
250        }
251
252        Err(Error::EventNotFound)
253    }
254
255    /// Create a batch of programs.
256    ///
257    /// A batch is a set of programs to be created within one function call.
258    /// Every entry of the `args` iterator is a tuple of parameters used in the
259    /// [`create_program_bytes`](Self::create_program_bytes) function. It is
260    /// useful when deploying a multi-program dApp.
261    pub async fn create_program_bytes_batch(
262        &self,
263        args: impl IntoIterator<Item = (CodeId, impl AsRef<[u8]>, impl AsRef<[u8]>, u64, u128)>,
264    ) -> Result<(Vec<Result<(MessageId, ActorId)>>, H256)> {
265        let calls: Vec<_> = args
266            .into_iter()
267            .map(|(code_id, salt, payload, gas_limit, value)| {
268                RuntimeCall::Gear(GearCall::create_program {
269                    code_id,
270                    salt: salt.as_ref().to_vec(),
271                    init_payload: payload.as_ref().to_vec(),
272                    gas_limit,
273                    value,
274                    keep_alive: false,
275                })
276            })
277            .collect();
278
279        let amount = calls.len();
280
281        let tx = self.0.calls().force_batch(calls).await?;
282        let mut res = Vec::with_capacity(amount);
283
284        for event in tx.wait_for_success().await?.iter() {
285            match event?.as_gear()? {
286                Event::Gear(GearEvent::MessageQueued {
287                    id,
288                    destination,
289                    entry: MessageEntry::Init,
290                    ..
291                }) => res.push(Ok((id, destination))),
292                Event::Utility(UtilityEvent::ItemFailed { error }) => {
293                    res.push(Err(self.0.api().decode_error(error).into()))
294                }
295                _ => (),
296            }
297        }
298
299        if res.len() == amount {
300            Ok((res, tx.block_hash()))
301        } else {
302            Err(Error::IncompleteBatchResult(res.len(), amount))
303        }
304    }
305
306    /// Same as [`create_program_bytes`](Self::create_program_bytes), but
307    /// initializes a newly created program with an encoded `payload`.
308    ///
309    /// # See also
310    ///
311    /// - [`upload_code`](Self::upload_code) function uploads a code and returns
312    ///   its identifier.
313    /// - [`upload_program`](Self::upload_program) function uploads a new
314    ///   program and initialize it.
315    pub async fn create_program(
316        &self,
317        code_id: CodeId,
318        salt: impl AsRef<[u8]>,
319        payload: impl Encode,
320        gas_limit: u64,
321        value: u128,
322    ) -> Result<(MessageId, ActorId, H256)> {
323        self.create_program_bytes(code_id, salt, payload.encode(), gas_limit, value)
324            .await
325    }
326
327    /// Migrates an active program identified by `src_program_id` onto another
328    /// node identified by `dest_node_api` and returns the migrated program
329    /// identifier. All source program data is taken at the time of
330    /// `src_block_hash` if it is specified or the most recent one.
331    pub async fn migrate_program(
332        &self,
333        src_program_id: ActorId,
334        src_block_hash: Option<H256>,
335        dest_node_api: &GearApi,
336    ) -> Result<ActorId> {
337        if dest_node_api.0.api().gprog(src_program_id).await.is_ok() {
338            return Err(Error::ProgramAlreadyExists(
339                src_program_id.as_ref().encode_hex(),
340            ));
341        }
342
343        let mut src_block_hash = src_block_hash;
344        if src_block_hash.is_none() {
345            src_block_hash = Some(self.last_block_hash().await?);
346        }
347
348        let dest_program_id = src_program_id;
349
350        // Collect data from the source program
351        let src_program_account_data = self
352            .account_data_at(src_program_id, src_block_hash)
353            .await
354            .or_else(|e| {
355            if let Error::GearSDK(GsdkError::StorageEntryNotFound) = e {
356                Ok(AccountData {
357                    free: 0u128,
358                    reserved: 0,
359                    frozen: 0,
360                    flags: gsdk::gear::runtime_types::pallet_balances::types::ExtraFlags(
361                        170141183460469231731687303715884105728,
362                    ),
363                })
364            } else {
365                Err(e)
366            }
367        })?;
368
369        let src_program_account_bank_data = self
370            .bank_data_at(src_program_id, src_block_hash)
371            .await
372            .or_else(|e| {
373                if let Error::GearSDK(GsdkError::StorageEntryNotFound) = e {
374                    Ok(BankAccount { gas: 0, value: 0 })
375                } else {
376                    Err(e)
377                }
378            })?;
379
380        let bank_address = self.bank_address().await?;
381
382        let src_bank_account_data = self
383            .account_data_at(bank_address.clone(), src_block_hash)
384            .await
385            .or_else(|e| {
386                if let Error::GearSDK(GsdkError::StorageEntryNotFound) = e {
387                    Ok(AccountData {
388                        free: 0u128,
389                        reserved: 0,
390                        frozen: 0,
391                        flags: gsdk::gear::runtime_types::pallet_balances::types::ExtraFlags(
392                            170141183460469231731687303715884105728,
393                        ),
394                    })
395                } else {
396                    Err(e)
397                }
398            })?;
399
400        let mut src_program = self
401            .0
402            .api()
403            .gprog_at(src_program_id, src_block_hash)
404            .await?;
405
406        let src_program_pages = self
407            .0
408            .api()
409            .gpages_at(
410                src_program_id,
411                Some(src_program.memory_infix),
412                src_block_hash,
413            )
414            .await?;
415
416        let src_program_reserved_gas_node_ids: Vec<GearGasNodeId> = src_program
417            .gas_reservation_map
418            .iter()
419            .map(|gr| GearGasNodeId::Reservation(gr.0))
420            .collect();
421
422        let src_program_reserved_gas_nodes = self
423            .0
424            .api()
425            .gas_nodes_at(src_program_reserved_gas_node_ids, src_block_hash)
426            .await?;
427
428        let mut src_program_reserved_gas_total = 0u64;
429        let mut accounts_with_reserved_funds = HashSet::new();
430        for gas_node in &src_program_reserved_gas_nodes {
431            if let GearGasNode::Reserved {
432                id, value, lock, ..
433            } = &gas_node.1
434            {
435                accounts_with_reserved_funds.insert(id.clone().into_substrate());
436                src_program_reserved_gas_total += value + lock.0[LockId::Reservation as usize];
437            } else {
438                unreachable!("Unexpected gas node type");
439            }
440        }
441
442        let src_code_id = src_program.code_id;
443
444        let src_instrumented_code = self
445            .0
446            .api()
447            .instrumented_code_storage_at(src_code_id, src_block_hash)
448            .await?;
449
450        let src_code_metadata = self
451            .0
452            .api()
453            .code_metadata_storage_at(src_code_id, src_block_hash)
454            .await?;
455
456        // Apply data to the target program
457        dest_node_api
458            .force_set_balance(
459                dest_program_id.into_account_id(),
460                src_program_account_data.free,
461            )
462            .await?;
463
464        dest_node_api
465            .force_set_balance(bank_address, src_bank_account_data.free)
466            .await?;
467
468        dest_node_api
469            .0
470            .storage()
471            .set_bank_account_storage(
472                src_program_id.into_account_id(),
473                src_program_account_bank_data,
474            )
475            .await?;
476
477        dest_node_api
478            .0
479            .storage()
480            .set_instrumented_code_storage(src_code_id, &src_instrumented_code)
481            .await?;
482
483        dest_node_api
484            .0
485            .storage()
486            .set_code_metadata_storage(src_code_id, &src_code_metadata)
487            .await?;
488
489        dest_node_api
490            .0
491            .storage()
492            .set_gas_nodes(&src_program_reserved_gas_nodes)
493            .await?;
494
495        for account_with_reserved_funds in accounts_with_reserved_funds {
496            let src_account_bank_data = self
497                .bank_data_at(account_with_reserved_funds.clone(), src_block_hash)
498                .await
499                .or_else(|e| {
500                    if let Error::GearSDK(GsdkError::StorageEntryNotFound) = e {
501                        Ok(BankAccount { gas: 0, value: 0 })
502                    } else {
503                        Err(e)
504                    }
505                })?;
506
507            let dest_account_data = dest_node_api
508                .account_data(account_with_reserved_funds.clone())
509                .await
510                .or_else(|e| {
511                    if let Error::GearSDK(GsdkError::StorageEntryNotFound) = e {
512                        Ok(AccountData {
513                            free: 0u128,
514                            reserved: 0,
515                            frozen: 0,
516                            flags: gsdk::gear::runtime_types::pallet_balances::types::ExtraFlags(
517                                170141183460469231731687303715884105728,
518                            ),
519                        })
520                    } else {
521                        Err(e)
522                    }
523                })?;
524            let dest_account_bank_data = self
525                .bank_data_at(account_with_reserved_funds.clone(), None)
526                .await
527                .or_else(|e| {
528                    if let Error::GearSDK(GsdkError::StorageEntryNotFound) = e {
529                        Ok(BankAccount { gas: 0, value: 0 })
530                    } else {
531                        Err(e)
532                    }
533                })?;
534
535            dest_node_api
536                .force_set_balance(
537                    account_with_reserved_funds.clone().into_account_id(),
538                    dest_account_data.free,
539                )
540                .await?;
541
542            dest_node_api
543                .0
544                .storage()
545                .set_bank_account_storage(
546                    account_with_reserved_funds.into_account_id(),
547                    BankAccount {
548                        gas: src_account_bank_data
549                            .gas
550                            .saturating_add(dest_account_bank_data.gas),
551                        value: src_account_bank_data
552                            .value
553                            .saturating_add(dest_account_bank_data.value),
554                    },
555                )
556                .await?;
557        }
558
559        let dest_gas_total_issuance =
560            dest_node_api.0.api().total_issuance().await.or_else(|e| {
561                if let GsdkError::StorageEntryNotFound = e {
562                    Ok(0)
563                } else {
564                    Err(e)
565                }
566            })?;
567
568        dest_node_api
569            .0
570            .storage()
571            .set_total_issuance(
572                dest_gas_total_issuance.saturating_add(src_program_reserved_gas_total),
573            )
574            .await?;
575
576        dest_node_api
577            .0
578            .storage()
579            .set_gpages(
580                dest_program_id,
581                src_program.memory_infix,
582                &src_program_pages,
583            )
584            .await?;
585
586        src_program.expiration_block = dest_node_api.last_block_number().await?;
587        dest_node_api
588            .0
589            .storage()
590            .set_gprog(dest_program_id, src_program)
591            .await?;
592
593        Ok(dest_program_id)
594    }
595
596    /// Get all pages with their data for program at specified block.
597    pub async fn get_program_pages_data_at(
598        &self,
599        program_id: ActorId,
600        block_hash: Option<H256>,
601    ) -> Result<BTreeMap<GearPage, PageBuf>> {
602        let pages_data = self.0.api().gpages_at(program_id, None, block_hash).await?;
603
604        let mut res = BTreeMap::new();
605        for (page, data) in pages_data.into_iter() {
606            res.insert(
607                GearPage::try_from(page).map_err(|err| anyhow!("{err}"))?,
608                data,
609            );
610        }
611
612        Ok(res)
613    }
614
615    /// Get specified pages with their data for program at specified block.
616    pub async fn get_program_specified_pages_data_at(
617        &self,
618        program_id: ActorId,
619        pages: impl Iterator<Item = GearPage>,
620        block_hash: Option<H256>,
621    ) -> Result<BTreeMap<GearPage, PageBuf>> {
622        let pages_data = self
623            .0
624            .api()
625            .specified_gpages_at(program_id, None, pages.map(Into::into), block_hash)
626            .await?;
627
628        let mut res = BTreeMap::new();
629        for (page, data) in pages_data.into_iter() {
630            res.insert(
631                GearPage::try_from(page).map_err(|err| anyhow::Error::msg(err.to_string()))?,
632                data,
633            );
634        }
635
636        Ok(res)
637    }
638
639    /// Save program (identified by `program_id`) memory dump to the file for
640    /// further restoring in gclient/gtest. Program memory dumped at the
641    /// time of `block_hash` if presented or the most recent block.
642    pub async fn save_program_memory_dump_at<P: AsRef<Path>>(
643        &self,
644        program_id: ActorId,
645        block_hash: Option<H256>,
646        file_path: P,
647    ) -> Result {
648        let program = self.0.api().gprog_at(program_id, block_hash).await?;
649
650        let program_pages = self
651            .0
652            .api()
653            .gpages_at(program_id, Some(program.memory_infix), block_hash)
654            .await?
655            .into_iter()
656            .map(|(page, data)| {
657                MemoryPageDump::new(
658                    GearPage::try_from(page)
659                        .unwrap_or_else(|_| panic!("Couldn't decode GearPage from u32: {page}")),
660                    PageBuf::decode(&mut &*data).expect("Couldn't decode PageBuf"),
661                )
662            })
663            .collect();
664
665        let program_account_data =
666            self.account_data_at(program_id, block_hash)
667                .await
668                .or_else(|e| {
669                    if let Error::GearSDK(GsdkError::StorageEntryNotFound) = e {
670                        Ok(AccountData {
671                            free: 0u128,
672                            reserved: 0,
673                            frozen: 0,
674                            flags: gsdk::gear::runtime_types::pallet_balances::types::ExtraFlags(
675                                170141183460469231731687303715884105728,
676                            ),
677                        })
678                    } else {
679                        Err(e)
680                    }
681                })?;
682
683        ProgramMemoryDump {
684            pages: program_pages,
685            balance: program_account_data.free,
686            reserved_balance: program_account_data.reserved,
687        }
688        .save_to_file(file_path);
689
690        Ok(())
691    }
692
693    /// Replace entire program memory with one saved earlier in gclient/gtest
694    pub async fn replace_program_memory<P: AsRef<Path>>(
695        &self,
696        program_id: ActorId,
697        file_path: P,
698    ) -> Result {
699        let memory_dump = ProgramMemoryDump::load_from_file(file_path);
700        let pages = memory_dump
701            .pages
702            .into_iter()
703            .map(|page| page.into_gear_page())
704            .map(|(page, page_buffer)| (page.into(), page_buffer))
705            .collect::<HashMap<u32, _>>();
706
707        self.force_set_balance(
708            MultiAddress::Id(program_id.into_account_id()),
709            memory_dump.balance,
710        )
711        .await?;
712
713        let program = self.0.api().gprog_at(program_id, None).await?;
714
715        self.0
716            .storage()
717            .set_gpages(program_id, program.memory_infix, &pages)
718            .await?;
719
720        Ok(())
721    }
722
723    /// Claim value from the mailbox message identified by `message_id`.
724    ///
725    /// Sends the
726    /// [`pallet_gear::claim_value`](https://docs.rs/pallet_gear/pallet/struct.Pallet.html#method.claim_value)
727    /// extrinsic.
728    ///
729    /// This function returns a tuple with value and block hash containing the
730    /// corresponding transaction.
731    ///
732    /// # See also
733    ///
734    /// - [`claim_value_batch`](Self::claim_value_batch) function claims a batch
735    ///   of values from the mailbox.
736    pub async fn claim_value(&self, message_id: MessageId) -> Result<(u128, H256)> {
737        let value = self
738            .get_mailbox_message(message_id)
739            .await?
740            .map(|(message, _interval)| message.value());
741
742        let tx = self.0.calls().claim_value(message_id).await?;
743
744        for event in tx.wait_for_success().await?.iter() {
745            if let Event::Gear(GearEvent::UserMessageRead { .. }) = event?.as_gear()? {
746                return Ok((
747                    value.expect("Data appearance guaranteed above"),
748                    tx.block_hash(),
749                ));
750            }
751        }
752
753        Err(Error::EventNotFound)
754    }
755
756    /// Claim a batch of values from the mailbox.
757    ///
758    /// A batch is a set of requests to be executed within one function call.
759    /// Every entry of the `args` iterator is a message identifier used in the
760    /// [`claim_value`](Self::claim_value) function. It is useful when
761    /// processing multiple replies in the mailbox at once.
762    pub async fn claim_value_batch(
763        &self,
764        args: impl IntoIterator<Item = MessageId> + Clone,
765    ) -> Result<(Vec<Result<u128>>, H256)> {
766        let message_ids: Vec<_> = args.clone().into_iter().collect();
767
768        let messages = futures::future::try_join_all(
769            message_ids.iter().map(|mid| self.get_mailbox_message(*mid)),
770        )
771        .await?;
772
773        let mut values: BTreeMap<_, _> = messages
774            .into_iter()
775            .flatten()
776            .map(|(msg, _interval)| (msg.id(), msg.value()))
777            .collect();
778
779        let calls: Vec<_> = args
780            .into_iter()
781            .map(|message_id| RuntimeCall::Gear(GearCall::claim_value { message_id }))
782            .collect();
783
784        let amount = calls.len();
785
786        let tx = self.0.calls().force_batch(calls).await?;
787        let mut res = Vec::with_capacity(amount);
788
789        for event in tx.wait_for_success().await?.iter() {
790            match event?.as_gear()? {
791                Event::Gear(GearEvent::UserMessageRead { id, .. }) => res.push(Ok(values
792                    .remove(&id)
793                    .expect("Data appearance guaranteed above"))),
794                Event::Utility(UtilityEvent::ItemFailed { error }) => {
795                    res.push(Err(self.0.api().decode_error(error).into()))
796                }
797                _ => (),
798            }
799        }
800
801        if res.len() == amount {
802            Ok((res, tx.block_hash()))
803        } else {
804            Err(Error::IncompleteBatchResult(res.len(), amount))
805        }
806    }
807
808    /// Send a message containing a byte slice `payload` to the `destination`.
809    ///
810    /// The message also contains the maximum `gas_limit` that can be spent and
811    /// the `value` to be transferred to the `destination`'s account.
812    ///
813    /// Sends the
814    /// [`pallet_gear::send_message`](https://docs.rs/pallet_gear/pallet/struct.Pallet.html#method.send_message)
815    /// extrinsic.
816    ///
817    /// This function returns a tuple with a new message identifier and a hash
818    /// of the block with the message enqueuing transaction.
819    ///
820    /// # See also
821    ///
822    /// - [`send_message`](Self::send_message) function sends a message with an
823    ///   encoded payload.
824    /// - [`send_message_bytes_batch`](Self::send_message_bytes_batch) function
825    ///   sends a batch of messages.
826    pub async fn send_message_bytes(
827        &self,
828        destination: ActorId,
829        payload: impl AsRef<[u8]>,
830        gas_limit: u64,
831        value: u128,
832    ) -> Result<(MessageId, H256)> {
833        let payload = payload.as_ref().to_vec();
834
835        let tx = self
836            .0
837            .calls()
838            .send_message(destination, payload, gas_limit, value)
839            .await?;
840
841        for event in tx.wait_for_success().await?.iter() {
842            if let Event::Gear(GearEvent::MessageQueued {
843                id,
844                entry: MessageEntry::Handle,
845                ..
846            }) = event?.as_gear()?
847            {
848                return Ok((id, tx.block_hash()));
849            }
850        }
851
852        Err(Error::EventNotFound)
853    }
854
855    /// Send a batch of messages.
856    ///
857    /// A batch is a set of messages to be sent within one function call. Every
858    /// entry of the `args` iterator is a tuple of parameters used in the
859    /// [`send_message_bytes`](Self::send_message_bytes) function. It is useful
860    /// when invoking several programs at once or sending a sequence of messages
861    /// to one program.
862    pub async fn send_message_bytes_batch(
863        &self,
864        args: impl IntoIterator<Item = (ActorId, impl AsRef<[u8]>, u64, u128)>,
865    ) -> Result<(Vec<Result<(MessageId, ActorId)>>, H256)> {
866        let calls: Vec<_> = args
867            .into_iter()
868            .map(|(destination, payload, gas_limit, value)| {
869                RuntimeCall::Gear(GearCall::send_message {
870                    destination,
871                    payload: payload.as_ref().to_vec(),
872                    gas_limit,
873                    value,
874                    keep_alive: false,
875                })
876            })
877            .collect();
878
879        let amount = calls.len();
880
881        let tx = self.0.calls().force_batch(calls).await?;
882        let mut res = Vec::with_capacity(amount);
883
884        for event in tx.wait_for_success().await?.iter() {
885            match event?.as_gear()? {
886                Event::Gear(GearEvent::MessageQueued {
887                    id,
888                    destination,
889                    entry: MessageEntry::Handle,
890                    ..
891                }) => res.push(Ok((id, destination))),
892                Event::Utility(UtilityEvent::ItemFailed { error }) => {
893                    res.push(Err(self.0.api().decode_error(error).into()))
894                }
895                _ => (),
896            }
897        }
898
899        if res.len() == amount {
900            Ok((res, tx.block_hash()))
901        } else {
902            Err(Error::IncompleteBatchResult(res.len(), amount))
903        }
904    }
905
906    /// Same as [`send_message_bytes`](Self::send_message_bytes), but sends a
907    /// message with encoded `payload`.
908    pub async fn send_message(
909        &self,
910        destination: ActorId,
911        payload: impl Encode,
912        gas_limit: u64,
913        value: u128,
914    ) -> Result<(MessageId, H256)> {
915        self.send_message_bytes(destination, payload.encode(), gas_limit, value)
916            .await
917    }
918
919    /// Send a reply containing a byte slice `payload` to the message identified
920    /// by `reply_to_id`.
921    ///
922    /// The reply also contains the maximum `gas_limit` that can be spent and
923    /// the `value` to be transferred to the destination's account.
924    ///
925    /// Sends the
926    /// [`pallet_gear::send_reply`](https://docs.rs/pallet_gear/pallet/struct.Pallet.html#method.send_reply)
927    /// extrinsic.
928    ///
929    /// This function returns a tuple with a new message identifier, transferred
930    /// value, and a hash of the block with the message enqueuing transaction.
931    ///
932    /// # See also
933    ///
934    /// - [`send_reply`](Self::send_reply) function sends a reply with an
935    ///   encoded payload.
936    /// - [`send_reply_bytes_batch`](Self::send_reply_bytes_batch) function send
937    ///   a batch of replies.
938    pub async fn send_reply_bytes(
939        &self,
940        reply_to_id: MessageId,
941        payload: impl AsRef<[u8]>,
942        gas_limit: u64,
943        value: u128,
944    ) -> Result<(MessageId, u128, H256)> {
945        let payload = payload.as_ref().to_vec();
946
947        let data = self.get_mailbox_message(reply_to_id).await?;
948
949        let tx = self
950            .0
951            .calls()
952            .send_reply(reply_to_id, payload, gas_limit, value)
953            .await?;
954
955        let events = tx.wait_for_success().await?;
956
957        let (message, _interval) = data.expect("Data appearance guaranteed above");
958
959        for event in events.iter() {
960            if let Event::Gear(GearEvent::MessageQueued {
961                id,
962                entry: MessageEntry::Reply(_),
963                ..
964            }) = event?.as_gear()?
965            {
966                return Ok((id, message.value(), tx.block_hash()));
967            }
968        }
969
970        Err(Error::EventNotFound)
971    }
972
973    /// Send a batch of replies.
974    ///
975    /// A batch is a set of replies to be sent within one function call. Every
976    /// entry of the `args` iterator is a tuple of parameters used in the
977    /// [`send_reply_bytes`](Self::send_reply_bytes) function. It is useful when
978    /// replying to several programs at once.
979    ///
980    /// The output for each call slightly differs from
981    /// [`send_reply_bytes`](Self::send_reply_bytes) as the destination
982    /// program id is also returned in the resulting tuple.
983    pub async fn send_reply_bytes_batch(
984        &self,
985        args: impl IntoIterator<Item = (MessageId, impl AsRef<[u8]>, u64, u128)> + Clone,
986    ) -> Result<(Vec<Result<(MessageId, ActorId, u128)>>, H256)> {
987        let message_ids: Vec<_> = args.clone().into_iter().map(|(mid, _, _, _)| mid).collect();
988
989        let messages = futures::future::try_join_all(
990            message_ids.iter().map(|mid| self.get_mailbox_message(*mid)),
991        )
992        .await?;
993
994        let mut values: BTreeMap<_, _> = messages
995            .into_iter()
996            .flatten()
997            .map(|(msg, _interval)| (msg.id(), msg.value()))
998            .collect();
999
1000        let calls: Vec<_> = args
1001            .into_iter()
1002            .map(|(reply_to_id, payload, gas_limit, value)| {
1003                RuntimeCall::Gear(GearCall::send_reply {
1004                    reply_to_id,
1005                    payload: payload.as_ref().to_vec(),
1006                    gas_limit,
1007                    value,
1008                    keep_alive: false,
1009                })
1010            })
1011            .collect();
1012
1013        let amount = calls.len();
1014
1015        let tx = self.0.calls().force_batch(calls).await?;
1016        let mut res = Vec::with_capacity(amount);
1017
1018        for event in tx.wait_for_success().await?.iter() {
1019            match event?.as_gear()? {
1020                Event::Gear(GearEvent::MessageQueued {
1021                    id,
1022                    entry: MessageEntry::Reply(reply_to_id),
1023                    destination,
1024                    ..
1025                }) => res.push(Ok((
1026                    id,
1027                    destination,
1028                    values
1029                        .remove(&reply_to_id)
1030                        .expect("Data appearance guaranteed above"),
1031                ))),
1032                Event::Utility(UtilityEvent::ItemFailed { error }) => {
1033                    res.push(Err(self.0.api().decode_error(error).into()))
1034                }
1035                _ => (),
1036            }
1037        }
1038
1039        if res.len() == amount {
1040            Ok((res, tx.block_hash()))
1041        } else {
1042            Err(Error::IncompleteBatchResult(res.len(), amount))
1043        }
1044    }
1045
1046    /// Same as [`send_reply_bytes`](Self::send_reply_bytes), but sends a reply
1047    /// with encoded `payload`.
1048    pub async fn send_reply(
1049        &self,
1050        reply_to_id: MessageId,
1051        payload: impl Encode,
1052        gas_limit: u64,
1053        value: u128,
1054    ) -> Result<(MessageId, u128, H256)> {
1055        self.send_reply_bytes(reply_to_id, payload.encode(), gas_limit, value)
1056            .await
1057    }
1058
1059    /// Upload Wasm `code` to be used for creating a new program.
1060    ///
1061    /// Sends the
1062    /// [`pallet_gear::upload_code`](https://docs.rs/pallet_gear/pallet/struct.Pallet.html#method.upload_code)
1063    /// extrinsic.
1064    ///
1065    /// This function returns a tuple with a code identifier and a hash of the
1066    /// block with the code uploading transaction. The code identifier can be
1067    /// used when creating a program using the
1068    /// [`create_program`](Self::create_program) function.
1069    ///
1070    /// # See also
1071    ///
1072    /// - [`create_program`](Self::create_program) function creates a program
1073    ///   from a previously uploaded code and initializes it.
1074    /// - [`upload_program`](Self::upload_program) function uploads a new
1075    ///   program and initialize it.
1076    pub async fn upload_code(&self, code: impl AsRef<[u8]>) -> Result<(CodeId, H256)> {
1077        let tx = self.0.calls().upload_code(code.as_ref().to_vec()).await?;
1078
1079        for event in tx.wait_for_success().await?.iter() {
1080            if let Event::Gear(GearEvent::CodeChanged {
1081                id,
1082                change: CodeChangeKind::Active { .. },
1083            }) = event?.as_gear()?
1084            {
1085                return Ok((id, tx.block_hash()));
1086            }
1087        }
1088
1089        Err(Error::EventNotFound)
1090    }
1091
1092    /// Upload a batch of codes.
1093    ///
1094    /// A batch is a set of codes to be uploaded within one function call. Every
1095    /// entry of the `args` iterator is a byte slice used in the
1096    /// [`upload_code`](Self::upload_code) function. It is useful when deploying
1097    /// a multi-program dApp.
1098    pub async fn upload_code_batch(
1099        &self,
1100        args: impl IntoIterator<Item = impl AsRef<[u8]>>,
1101    ) -> Result<(Vec<Result<CodeId>>, H256)> {
1102        let calls: Vec<_> = args
1103            .into_iter()
1104            .map(|code| {
1105                RuntimeCall::Gear(GearCall::upload_code {
1106                    code: code.as_ref().to_vec(),
1107                })
1108            })
1109            .collect();
1110
1111        let amount = calls.len();
1112
1113        let tx = self.0.calls().force_batch(calls).await?;
1114        let mut res = Vec::with_capacity(amount);
1115
1116        for event in tx.wait_for_success().await?.iter() {
1117            match event?.as_gear()? {
1118                Event::Gear(GearEvent::CodeChanged {
1119                    id,
1120                    change: CodeChangeKind::Active { .. },
1121                }) => {
1122                    res.push(Ok(id));
1123                }
1124                Event::Utility(UtilityEvent::ItemFailed { error }) => {
1125                    res.push(Err(self.0.api().decode_error(error).into()))
1126                }
1127                _ => (),
1128            }
1129        }
1130
1131        if res.len() == amount {
1132            Ok((res, tx.block_hash()))
1133        } else {
1134            Err(Error::IncompleteBatchResult(res.len(), amount))
1135        }
1136    }
1137
1138    /// Upload Wasm code from the file referenced by `path` to be used for
1139    /// creating a new program.
1140    ///
1141    /// Same as [`upload_code`](Self::upload_code), but reads the code from a
1142    /// file instead of using a byte vector.
1143    ///
1144    /// Works with absolute and relative paths (relative to the root dir of the
1145    /// repo).
1146    pub async fn upload_code_by_path(&self, path: impl AsRef<Path>) -> Result<(CodeId, H256)> {
1147        let code = utils::code_from_os(path)?;
1148        self.upload_code(code).await
1149    }
1150
1151    /// Upload a new program and initialize it with a byte slice `payload`.
1152    ///
1153    /// Sends the
1154    /// [`pallet_gear::upload_program`](https://docs.rs/pallet_gear/pallet/struct.Pallet.html#method.upload_program)
1155    /// extrinsic.
1156    ///
1157    /// Parameters:
1158    ///
1159    /// - `code` is the byte slice containing a binary Wasm code of the program;
1160    /// - `salt` is the arbitrary data needed to generate an address for a new
1161    ///   program (control of salt uniqueness is entirely on the function
1162    ///   caller’s side);
1163    /// - `payload` vector contains data to be processed in the `init` function
1164    ///   of the newly deployed "child" program;
1165    /// - `gas_limit` is the maximum gas amount allowed to spend for the program
1166    ///   creation and initialization;
1167    /// - `value` to be transferred to the program's account during
1168    ///   initialization.
1169    ///
1170    /// # See also
1171    ///
1172    /// - [`create_program_bytes`](Self::create_program_bytes) function creates
1173    ///   a program from a previously uploaded code.
1174    /// - [`upload_code`](Self::upload_code) function uploads a code and returns
1175    ///   its identifier.
1176    /// - [`upload_program`](Self::upload_program) function uploads a program
1177    ///   and initializes it with an encoded payload.
1178    pub async fn upload_program_bytes(
1179        &self,
1180        code: impl AsRef<[u8]>,
1181        salt: impl AsRef<[u8]>,
1182        payload: impl AsRef<[u8]>,
1183        gas_limit: u64,
1184        value: u128,
1185    ) -> Result<(MessageId, ActorId, H256)> {
1186        let code = code.as_ref().to_vec();
1187        let salt = salt.as_ref().to_vec();
1188        let payload = payload.as_ref().to_vec();
1189
1190        let tx = self
1191            .0
1192            .calls()
1193            .upload_program(code, salt, payload, gas_limit, value)
1194            .await?;
1195
1196        for event in tx.wait_for_success().await?.iter() {
1197            if let Event::Gear(GearEvent::MessageQueued {
1198                id,
1199                destination,
1200                entry: MessageEntry::Init,
1201                ..
1202            }) = event?.as_gear()?
1203            {
1204                return Ok((id, destination, tx.block_hash()));
1205            }
1206        }
1207
1208        Err(Error::EventNotFound)
1209    }
1210
1211    /// Upload a batch of programs.
1212    ///
1213    /// A batch is a set of programs to be uploaded within one function call.
1214    /// Every entry of the `args` iterator is a tuple used in the
1215    /// [`upload_program_bytes`](Self::upload_program_bytes) function. It is
1216    /// useful when deploying a multi-program dApp.
1217    pub async fn upload_program_bytes_batch(
1218        &self,
1219        args: impl IntoIterator<
1220            Item = (
1221                impl AsRef<[u8]>,
1222                impl AsRef<[u8]>,
1223                impl AsRef<[u8]>,
1224                u64,
1225                u128,
1226            ),
1227        >,
1228    ) -> Result<(Vec<Result<(MessageId, ActorId)>>, H256)> {
1229        let calls: Vec<_> = args
1230            .into_iter()
1231            .map(|(code, salt, payload, gas_limit, value)| {
1232                RuntimeCall::Gear(GearCall::upload_program {
1233                    code: code.as_ref().to_vec(),
1234                    salt: salt.as_ref().to_vec(),
1235                    init_payload: payload.as_ref().to_vec(),
1236                    gas_limit,
1237                    value,
1238                    keep_alive: false,
1239                })
1240            })
1241            .collect();
1242
1243        let amount = calls.len();
1244
1245        let tx = self.0.calls().force_batch(calls).await?;
1246        let mut res = Vec::with_capacity(amount);
1247
1248        for event in tx.wait_for_success().await?.iter() {
1249            match event?.as_gear()? {
1250                Event::Gear(GearEvent::MessageQueued {
1251                    id,
1252                    destination,
1253                    entry: MessageEntry::Init,
1254                    ..
1255                }) => res.push(Ok((id, destination))),
1256                Event::Utility(UtilityEvent::ItemFailed { error }) => {
1257                    res.push(Err(self.0.api().decode_error(error).into()))
1258                }
1259                _ => (),
1260            }
1261        }
1262
1263        if res.len() == amount {
1264            Ok((res, tx.block_hash()))
1265        } else {
1266            Err(Error::IncompleteBatchResult(res.len(), amount))
1267        }
1268    }
1269
1270    /// Upload a new program from the file referenced by `path` and initialize
1271    /// it with a byte slice `payload`.
1272    ///
1273    /// Same as [`upload_program_bytes`](Self::upload_program_bytes), but reads
1274    /// the program from a file instead of using a byte vector.
1275    ///
1276    /// Works with absolute and relative paths (relative to the root dir of the
1277    /// repo).
1278    pub async fn upload_program_bytes_by_path(
1279        &self,
1280        path: impl AsRef<Path>,
1281        salt: impl AsRef<[u8]>,
1282        payload: impl AsRef<[u8]>,
1283        gas_limit: u64,
1284        value: u128,
1285    ) -> Result<(MessageId, ActorId, H256)> {
1286        let code = utils::code_from_os(path)?;
1287        self.upload_program_bytes(code, salt, payload, gas_limit, value)
1288            .await
1289    }
1290
1291    /// Same as [`upload_program_bytes`](Self::upload_program_bytes), but
1292    /// initializes a newly uploaded program with an encoded `payload`.
1293    ///
1294    /// # See also
1295    ///
1296    /// - [`create_program`](Self::create_program) function creates a program
1297    ///   from a previously uploaded code.
1298    /// - [`upload_code`](Self::upload_code) function uploads a code and returns
1299    ///   its identifier.
1300    pub async fn upload_program(
1301        &self,
1302        code: impl AsRef<[u8]>,
1303        salt: impl AsRef<[u8]>,
1304        payload: impl Encode,
1305        gas_limit: u64,
1306        value: u128,
1307    ) -> Result<(MessageId, ActorId, H256)> {
1308        self.upload_program_bytes(code, salt, payload.encode(), gas_limit, value)
1309            .await
1310    }
1311
1312    /// Upload a new program from the file referenced by `path` and initialize
1313    /// it with an encoded `payload`.
1314    ///
1315    /// Same as [`upload_program`](Self::upload_program), but reads the program
1316    /// from a file instead of using a byte vector.
1317    ///
1318    /// Works with absolute and relative paths (relative to the root dir of the
1319    /// repo).
1320    pub async fn upload_program_by_path(
1321        &self,
1322        path: impl AsRef<Path>,
1323        salt: impl AsRef<[u8]>,
1324        payload: impl Encode,
1325        gas_limit: u64,
1326        value: u128,
1327    ) -> Result<(MessageId, ActorId, H256)> {
1328        let code = utils::code_from_os(path)?;
1329        self.upload_program(code, salt, payload, gas_limit, value)
1330            .await
1331    }
1332
1333    fn process_set_code(&self, events: &ExtrinsicEvents<GearConfig>) -> Result<()> {
1334        for event in events.iter() {
1335            let event = event?.as_gear()?;
1336            if let Event::System(SystemEvent::CodeUpdated) = event {
1337                return Ok(());
1338            }
1339        }
1340
1341        Err(Error::EventNotFound)
1342    }
1343
1344    /// Upgrade the runtime with the `code` containing the Wasm code of the new
1345    /// runtime.
1346    ///
1347    /// Sends the
1348    /// [`pallet_system::set_code`](https://crates.parity.io/frame_system/pallet/struct.Pallet.html#method.set_code)
1349    /// extrinsic.
1350    pub async fn set_code(&self, code: impl AsRef<[u8]>) -> Result<H256> {
1351        let (block_hash, events) = self
1352            .0
1353            .calls()
1354            .sudo_unchecked_weight(
1355                RuntimeCall::System(SystemCall::set_code {
1356                    code: code.as_ref().to_vec(),
1357                }),
1358                Weight {
1359                    ref_time: 0,
1360                    proof_size: 0,
1361                },
1362            )
1363            .await?;
1364        self.process_set_code(&events)?;
1365        Ok(block_hash)
1366    }
1367
1368    /// Upgrade the runtime by reading the code from the file located at the
1369    /// `path`.
1370    ///
1371    /// Same as [`set_code`](Self::set_code), but reads the runtime code from a
1372    /// file instead of using a byte vector.
1373    pub async fn set_code_by_path(&self, path: impl AsRef<Path>) -> Result<H256> {
1374        let code = utils::code_from_os(path)?;
1375        self.set_code(code).await
1376    }
1377
1378    /// Upgrade the runtime with the `code` containing the Wasm code of the new
1379    /// runtime but **without** checks.
1380    ///
1381    /// Sends the
1382    /// [`pallet_system::set_code_without_checks`](https://crates.parity.io/frame_system/pallet/struct.Pallet.html#method.set_code_without_checks)
1383    /// extrinsic.
1384    pub async fn set_code_without_checks(&self, code: impl AsRef<[u8]>) -> Result<H256> {
1385        let (block_hash, events) = self
1386            .0
1387            .calls()
1388            .sudo_unchecked_weight(
1389                RuntimeCall::System(SystemCall::set_code_without_checks {
1390                    code: code.as_ref().to_vec(),
1391                }),
1392                Weight {
1393                    ref_time: 0,
1394                    proof_size: 0,
1395                },
1396            )
1397            .await?;
1398        self.process_set_code(&events)?;
1399        Ok(block_hash)
1400    }
1401
1402    /// Upgrade the runtime by reading the code from the file located at the
1403    /// `path`.
1404    ///
1405    /// Same as [`set_code_without_checks`](Self::set_code_without_checks), but
1406    /// reads the runtime code from a file instead of using a byte vector.
1407    pub async fn set_code_without_checks_by_path(&self, path: impl AsRef<Path>) -> Result<H256> {
1408        let code = utils::code_from_os(path)?;
1409        self.set_code_without_checks(code).await
1410    }
1411
1412    /// Set the free balance of the `to` account to `new_free`.
1413    ///
1414    /// Sends the [`pallet_balances::set_balance`](https://crates.parity.io/pallet_balances/pallet/struct.Pallet.html#method.set_balance) extrinsic.
1415    pub async fn force_set_balance(
1416        &self,
1417        to: impl Into<MultiAddress<AccountId32, ()>>,
1418        new_free: u128,
1419    ) -> Result<H256> {
1420        let events = self
1421            .0
1422            .calls()
1423            .sudo_unchecked_weight(
1424                RuntimeCall::Balances(BalancesCall::force_set_balance {
1425                    who: to.into().into_subxt(),
1426                    new_free,
1427                }),
1428                Weight {
1429                    ref_time: 0,
1430                    // # TODO
1431                    //
1432                    // Check this field
1433                    proof_size: Default::default(),
1434                },
1435            )
1436            .await?;
1437        Ok(events.0)
1438    }
1439
1440    /// Same as [`upload_code`](Self::upload_code), but upload code
1441    /// using voucher.
1442    pub async fn upload_code_with_voucher(
1443        &self,
1444        voucher_id: VoucherId,
1445        code: impl AsRef<[u8]>,
1446    ) -> Result<(CodeId, H256)> {
1447        let tx = self
1448            .0
1449            .calls()
1450            .upload_code_with_voucher(voucher_id, code.as_ref().to_vec())
1451            .await?;
1452
1453        for event in tx.wait_for_success().await?.iter() {
1454            if let Event::Gear(GearEvent::CodeChanged {
1455                id,
1456                change: CodeChangeKind::Active { .. },
1457            }) = event?.as_gear()?
1458            {
1459                return Ok((id, tx.block_hash()));
1460            }
1461        }
1462
1463        Err(Error::EventNotFound)
1464    }
1465
1466    /// Same as [`send_message_bytes`](Self::send_message_bytes), but sends a
1467    /// message using voucher.
1468    pub async fn send_message_bytes_with_voucher(
1469        &self,
1470        voucher_id: VoucherId,
1471        destination: ActorId,
1472        payload: impl AsRef<[u8]>,
1473        gas_limit: u64,
1474        value: u128,
1475        keep_alive: bool,
1476    ) -> Result<(MessageId, H256)> {
1477        let payload = payload.as_ref().to_vec();
1478
1479        let tx = self
1480            .0
1481            .calls()
1482            .send_message_with_voucher(
1483                voucher_id,
1484                destination,
1485                payload,
1486                gas_limit,
1487                value,
1488                keep_alive,
1489            )
1490            .await?;
1491
1492        for event in tx.wait_for_success().await?.iter() {
1493            if let Event::Gear(GearEvent::MessageQueued {
1494                id,
1495                entry: MessageEntry::Handle,
1496                ..
1497            }) = event?.as_gear()?
1498            {
1499                return Ok((id, tx.block_hash()));
1500            }
1501        }
1502
1503        Err(Error::EventNotFound)
1504    }
1505
1506    /// Same as [`send_message_bytes_with_voucher`](Self::send_message_bytes_with_voucher), but sends a
1507    /// message with encoded `payload`.
1508    pub async fn send_message_with_voucher(
1509        &self,
1510        voucher_id: VoucherId,
1511        destination: ActorId,
1512        payload: impl Encode,
1513        gas_limit: u64,
1514        value: u128,
1515        keep_alive: bool,
1516    ) -> Result<(MessageId, H256)> {
1517        self.send_message_bytes_with_voucher(
1518            voucher_id,
1519            destination,
1520            payload.encode(),
1521            gas_limit,
1522            value,
1523            keep_alive,
1524        )
1525        .await
1526    }
1527
1528    /// Same as [`send_reply_bytes`](Self::send_reply_bytes), but sends a reply
1529    /// using voucher.
1530    pub async fn send_reply_bytes_with_voucher(
1531        &self,
1532        voucher_id: VoucherId,
1533        reply_to_id: MessageId,
1534        payload: impl AsRef<[u8]>,
1535        gas_limit: u64,
1536        value: u128,
1537        keep_alive: bool,
1538    ) -> Result<(MessageId, u128, H256)> {
1539        let payload = payload.as_ref().to_vec();
1540
1541        let data = self.get_mailbox_message(reply_to_id).await?;
1542
1543        let tx = self
1544            .0
1545            .calls()
1546            .send_reply_with_voucher(
1547                voucher_id,
1548                reply_to_id,
1549                payload,
1550                gas_limit,
1551                value,
1552                keep_alive,
1553            )
1554            .await?;
1555
1556        let events = tx.wait_for_success().await?;
1557
1558        let (message, _interval) = data.expect("Data appearance guaranteed above");
1559
1560        for event in events.iter() {
1561            if let Event::Gear(GearEvent::MessageQueued {
1562                id,
1563                entry: MessageEntry::Reply(_),
1564                ..
1565            }) = event?.as_gear()?
1566            {
1567                return Ok((id, message.value(), tx.block_hash()));
1568            }
1569        }
1570
1571        Err(Error::EventNotFound)
1572    }
1573
1574    /// Same as [`send_reply_bytes_with_voucher`](Self::send_reply_bytes_with_voucher), but sends a reply
1575    /// with encoded `payload`.
1576    pub async fn send_reply_with_voucher(
1577        &self,
1578        voucher_id: VoucherId,
1579        reply_to_id: MessageId,
1580        payload: impl Encode,
1581        gas_limit: u64,
1582        value: u128,
1583        keep_alive: bool,
1584    ) -> Result<(MessageId, u128, H256)> {
1585        self.send_reply_bytes_with_voucher(
1586            voucher_id,
1587            reply_to_id,
1588            payload.encode(),
1589            gas_limit,
1590            value,
1591            keep_alive,
1592        )
1593        .await
1594    }
1595}