Skip to main content

ic_utils/interfaces/
management_canister.rs

1//! The canister interface for the IC management canister. See the [specification][spec] for full documentation of the interface.
2//!
3//! [spec]: https://internetcomputer.org/docs/current/references/ic-interface-spec#ic-management-canister
4
5use crate::{
6    call::{AsyncCall, SyncCall},
7    Canister,
8};
9use candid::{utils::ArgumentDecoder, Decode, Encode};
10use ic_agent::{agent::EffectiveId, export::Principal, Agent, AgentError};
11use ic_management_canister_types::{
12    CanisterIdRecord, DeleteCanisterSnapshotArgs, LoadCanisterSnapshotArgs,
13    ProvisionalTopUpCanisterArgs, ReadCanisterSnapshotDataArgs, ReadCanisterSnapshotMetadataArgs,
14    TakeCanisterSnapshotArgs, UploadCanisterSnapshotDataArgs, UploadCanisterSnapshotMetadataArgs,
15    UploadChunkArgs,
16};
17// Re-export the types that are used be defined in this file.
18pub use ic_management_canister_types::{
19    CanisterIdRange, CanisterLogFilter, CanisterLogRecord, CanisterMetadataArgs,
20    CanisterMetadataResult, CanisterMetricsArgs, CanisterMetricsResult, CanisterStatusResult,
21    CanisterStatusType, CanisterTimer, ChunkHash, CyclesConsumed, DefiniteCanisterSettings,
22    FetchCanisterLogsArgs, FetchCanisterLogsResult, ListCanistersResult, LogVisibility,
23    MemoryMetrics, OnLowWasmMemoryHookStatus, QueryStats, ReadCanisterSnapshotDataResult,
24    ReadCanisterSnapshotMetadataResult, RenameCanisterRecord, RenameToRecord, Snapshot,
25    SnapshotDataKind, SnapshotDataOffset, SnapshotMetadataGlobal, SnapshotSource,
26    StoredChunksResult, UploadCanisterSnapshotMetadataResult, UploadChunkResult,
27};
28use std::{convert::AsRef, marker::PhantomData, ops::Deref};
29use strum_macros::{AsRefStr, Display, EnumString, IntoStaticStr};
30
31pub mod attributes;
32pub mod builders;
33
34#[doc(inline)]
35pub use builders::{
36    CreateCanisterBuilder, InstallBuilder, InstallChunkedCodeBuilder, InstallCodeBuilder,
37    UpdateSettingsBuilder,
38};
39
40/// The IC management canister.
41#[derive(Debug, Clone)]
42pub struct ManagementCanister<'agent>(Canister<'agent>);
43
44impl<'agent> Deref for ManagementCanister<'agent> {
45    type Target = Canister<'agent>;
46    fn deref(&self) -> &Self::Target {
47        &self.0
48    }
49}
50
51/// All the known methods of the management canister.
52#[derive(AsRefStr, Debug, EnumString, Display, IntoStaticStr)]
53#[strum(serialize_all = "snake_case")]
54pub enum MgmtMethod {
55    /// See [`ManagementCanister::create_canister`].
56    CreateCanister,
57    /// See [`ManagementCanister::install_code`].
58    InstallCode,
59    /// See [`ManagementCanister::start_canister`].
60    StartCanister,
61    /// See [`ManagementCanister::stop_canister`].
62    StopCanister,
63    /// See [`ManagementCanister::canister_status`].
64    CanisterStatus,
65    /// See [`ManagementCanister::delete_canister`].
66    DeleteCanister,
67    /// See [`ManagementCanister::deposit_cycles`].
68    DepositCycles,
69    /// See [`ManagementCanister::raw_rand`].
70    RawRand,
71    /// See [`CreateCanisterBuilder::as_provisional_create_with_amount`].
72    ProvisionalCreateCanisterWithCycles,
73    /// See [`ManagementCanister::provisional_top_up_canister`].
74    ProvisionalTopUpCanister,
75    /// See [`ManagementCanister::uninstall_code`].
76    UninstallCode,
77    /// See [`ManagementCanister::update_settings`].
78    UpdateSettings,
79    /// See [`ManagementCanister::upload_chunk`].
80    UploadChunk,
81    /// See [`ManagementCanister::clear_chunk_store`].
82    ClearChunkStore,
83    /// See [`ManagementCanister::stored_chunks`].
84    StoredChunks,
85    /// See [`ManagementCanister::install_chunked_code`].
86    InstallChunkedCode,
87    /// See [`ManagementCanister::fetch_canister_logs`].
88    FetchCanisterLogs,
89    /// See [`ManagementCanister::take_canister_snapshot`].
90    TakeCanisterSnapshot,
91    /// See [`ManagementCanister::load_canister_snapshot`].
92    LoadCanisterSnapshot,
93    /// See [`ManagementCanister::list_canister_snapshots`].
94    ListCanisterSnapshots,
95    /// See [`ManagementCanister::delete_canister_snapshot`].
96    DeleteCanisterSnapshot,
97    /// See [`ManagementCanister::read_canister_snapshot_metadata`].
98    ReadCanisterSnapshotMetadata,
99    /// See [`ManagementCanister::read_canister_snapshot_data`].
100    ReadCanisterSnapshotData,
101    /// See [`ManagementCanister::upload_canister_snapshot_metadata`].
102    UploadCanisterSnapshotMetadata,
103    /// See [`ManagementCanister::upload_canister_snapshot_data`].
104    UploadCanisterSnapshotData,
105    /// There is no corresponding agent function as only canisters can call it.
106    EcdsaPublicKey,
107    /// There is no corresponding agent function as only canisters can call it.
108    SignWithEcdsa,
109    /// There is no corresponding agent function as only canisters can call it. Use [`BitcoinCanister`](super::BitcoinCanister) instead.
110    BitcoinGetBalance,
111    /// There is no corresponding agent function as only canisters can call it. Use [`BitcoinCanister`](super::BitcoinCanister) instead.
112    BitcoinGetUtxos,
113    /// There is no corresponding agent function as only canisters can call it. Use [`BitcoinCanister`](super::BitcoinCanister) instead.
114    BitcoinSendTransaction,
115    /// There is no corresponding agent function as only canisters can call it. Use [`BitcoinCanister`](super::BitcoinCanister) instead.
116    BitcoinGetCurrentFeePercentiles,
117    /// There is no corresponding agent function as only canisters can call it. Use [`BitcoinCanister`](super::BitcoinCanister) instead.
118    BitcoinGetBlockHeaders,
119    /// There is no corresponding agent function as only canisters can call it.
120    NodeMetricsHistory,
121    /// There is no corresponding agent function as only canisters can call it.
122    CanisterInfo,
123    /// See [`ManagementCanister::canister_metadata`].
124    CanisterMetadata,
125    /// See [`ManagementCanister::canister_metrics`].
126    CanisterMetrics,
127    /// See [`ManagementCanister::list_canisters`].
128    ListCanisters,
129}
130
131impl<'agent> ManagementCanister<'agent> {
132    /// Create an instance of a `ManagementCanister` interface pointing to the specified Canister ID.
133    pub fn create(agent: &'agent Agent) -> Self {
134        Self(
135            Canister::builder()
136                .with_agent(agent)
137                .with_canister_id(Principal::management_canister())
138                .build()
139                .unwrap(),
140        )
141    }
142
143    /// Create a `ManagementCanister` interface from an existing canister object.
144    pub fn from_canister(canister: Canister<'agent>) -> Self {
145        Self(canister)
146    }
147}
148
149#[doc(hidden)]
150#[deprecated(since = "0.42.0", note = "Please use CanisterStatusResult instead")]
151pub type StatusCallResult = CanisterStatusResult;
152
153#[doc(hidden)]
154#[deprecated(since = "0.42.0", note = "Please use CanisterStatusType instead")]
155pub type CanisterStatus = CanisterStatusType;
156
157#[doc(hidden)]
158#[deprecated(since = "0.42.0", note = "Please use FetchCanisterLogsResult instead")]
159pub type FetchCanisterLogsResponse = FetchCanisterLogsResult;
160
161#[doc(hidden)]
162#[deprecated(since = "0.42.0", note = "Please use StoredChunksResult instead")]
163pub type StoreChunksResult = StoredChunksResult;
164
165#[doc(hidden)]
166#[deprecated(
167    since = "0.42.0",
168    note = "Please use ReadCanisterSnapshotMetadataResult instead"
169)]
170pub type SnapshotMetadata = ReadCanisterSnapshotMetadataResult;
171
172#[doc(hidden)]
173#[deprecated(
174    since = "0.42.0",
175    note = "Please use ReadCanisterSnapshotDataResult instead"
176)]
177pub type SnapshotDataResult = ReadCanisterSnapshotDataResult;
178
179#[doc(hidden)]
180#[deprecated(
181    since = "0.42.0",
182    note = "Please use UploadCanisterSnapshotMetadataResult instead"
183)]
184pub type CanisterSnapshotId = UploadCanisterSnapshotMetadataResult;
185
186impl<'agent> ManagementCanister<'agent> {
187    /// Get the status of a canister.
188    ///
189    /// Issued as a (fast, non-replicated) query call by default; call
190    /// [`QueryOrUpdateCall::as_update`] on the returned builder for a replicated
191    /// update call instead.
192    pub fn canister_status(
193        &self,
194        canister_id: &Principal,
195    ) -> QueryOrUpdateCall<'agent, '_, (CanisterStatusResult,)> {
196        QueryOrUpdateCall::new(&self.0, MgmtMethod::CanisterStatus.into(), *canister_id)
197    }
198
199    /// Create a canister.
200    pub fn create_canister<'canister>(&'canister self) -> CreateCanisterBuilder<'agent, 'canister> {
201        CreateCanisterBuilder::builder(self)
202    }
203
204    /// This method deposits the cycles included in this call into the specified canister.
205    /// Only the controller of the canister can deposit cycles.
206    pub fn deposit_cycles(&self, canister_id: &Principal) -> impl 'agent + AsyncCall<Value = ()> {
207        self.update(MgmtMethod::DepositCycles.as_ref())
208            .with_arg(CanisterIdRecord {
209                canister_id: *canister_id,
210            })
211            .with_effective_canister_id(canister_id.to_owned())
212            .build()
213    }
214
215    /// Deletes a canister.
216    pub fn delete_canister(&self, canister_id: &Principal) -> impl 'agent + AsyncCall<Value = ()> {
217        self.update(MgmtMethod::DeleteCanister.as_ref())
218            .with_arg(CanisterIdRecord {
219                canister_id: *canister_id,
220            })
221            .with_effective_canister_id(canister_id.to_owned())
222            .build()
223    }
224
225    /// Until developers can convert real ICP tokens to a top up an existing canister,
226    /// the system provides the `provisional_top_up_canister` method.
227    /// It adds amount cycles to the balance of canister identified by amount
228    /// (implicitly capping it at `MAX_CANISTER_BALANCE`).
229    pub fn provisional_top_up_canister(
230        &self,
231        canister_id: &Principal,
232        top_up_args: &ProvisionalTopUpCanisterArgs,
233    ) -> impl 'agent + AsyncCall<Value = ()> {
234        self.update(MgmtMethod::ProvisionalTopUpCanister.as_ref())
235            .with_arg(top_up_args)
236            .with_effective_canister_id(canister_id.to_owned())
237            .build()
238    }
239
240    /// This method takes no input and returns 32 pseudo-random bytes to the caller.
241    /// The return value is unknown to any part of the IC at time of the submission of this call.
242    /// A new return value is generated for each call to this method.
243    pub fn raw_rand(&self) -> impl 'agent + AsyncCall<Value = (Vec<u8>,)> {
244        self.update(MgmtMethod::RawRand.as_ref())
245            .build()
246            .map(|result: (Vec<u8>,)| (result.0,))
247    }
248
249    /// Starts a canister.
250    pub fn start_canister(&self, canister_id: &Principal) -> impl 'agent + AsyncCall<Value = ()> {
251        self.update(MgmtMethod::StartCanister.as_ref())
252            .with_arg(CanisterIdRecord {
253                canister_id: *canister_id,
254            })
255            .with_effective_canister_id(canister_id.to_owned())
256            .build()
257    }
258
259    /// Stop a canister.
260    pub fn stop_canister(&self, canister_id: &Principal) -> impl 'agent + AsyncCall<Value = ()> {
261        self.update(MgmtMethod::StopCanister.as_ref())
262            .with_arg(CanisterIdRecord {
263                canister_id: *canister_id,
264            })
265            .with_effective_canister_id(canister_id.to_owned())
266            .build()
267    }
268
269    /// This method removes a canister’s code and state, making the canister empty again.
270    /// Only the controller of the canister can uninstall code.
271    /// Uninstalling a canister’s code will reject all calls that the canister has not yet responded to,
272    /// and drop the canister’s code and state.
273    /// Outstanding responses to the canister will not be processed, even if they arrive after code has been installed again.
274    /// The canister is now empty. In particular, any incoming or queued calls will be rejected.
275    //// A canister after uninstalling retains its cycles balance, controller, status, and allocations.
276    pub fn uninstall_code(&self, canister_id: &Principal) -> impl 'agent + AsyncCall<Value = ()> {
277        self.update(MgmtMethod::UninstallCode.as_ref())
278            .with_arg(CanisterIdRecord {
279                canister_id: *canister_id,
280            })
281            .with_effective_canister_id(canister_id.to_owned())
282            .build()
283    }
284
285    /// Install a canister, with all the arguments necessary for creating the canister.
286    pub fn install_code<'canister>(
287        &'canister self,
288        canister_id: &Principal,
289        wasm: &'canister [u8],
290    ) -> InstallCodeBuilder<'agent, 'canister> {
291        InstallCodeBuilder::builder(self, canister_id, wasm)
292    }
293
294    /// Update one or more of a canisters settings (i.e its controller, compute allocation, or memory allocation.)
295    pub fn update_settings<'canister>(
296        &'canister self,
297        canister_id: &Principal,
298    ) -> UpdateSettingsBuilder<'agent, 'canister> {
299        UpdateSettingsBuilder::builder(self, canister_id)
300    }
301
302    /// Upload a chunk of a WASM module to a canister's chunked WASM storage.
303    pub fn upload_chunk(
304        &self,
305        canister_id: &Principal,
306        upload_chunk_args: &UploadChunkArgs,
307    ) -> impl 'agent + AsyncCall<Value = (UploadChunkResult,)> {
308        self.update(MgmtMethod::UploadChunk.as_ref())
309            .with_arg(upload_chunk_args)
310            .with_effective_canister_id(*canister_id)
311            .build()
312    }
313
314    /// Clear a canister's chunked WASM storage.
315    pub fn clear_chunk_store(
316        &self,
317        canister_id: &Principal,
318    ) -> impl 'agent + AsyncCall<Value = ()> {
319        self.update(MgmtMethod::ClearChunkStore.as_ref())
320            .with_arg(CanisterIdRecord {
321                canister_id: *canister_id,
322            })
323            .with_effective_canister_id(*canister_id)
324            .build()
325    }
326
327    /// Get a list of the hashes of a canister's stored WASM chunks
328    pub fn stored_chunks(
329        &self,
330        canister_id: &Principal,
331    ) -> impl 'agent + AsyncCall<Value = (StoredChunksResult,)> {
332        self.update(MgmtMethod::StoredChunks.as_ref())
333            .with_arg(CanisterIdRecord {
334                canister_id: *canister_id,
335            })
336            .with_effective_canister_id(*canister_id)
337            .build()
338    }
339
340    /// Install a canister module previously uploaded in chunks via [`upload_chunk`](Self::upload_chunk).
341    pub fn install_chunked_code<'canister>(
342        &'canister self,
343        canister_id: &Principal,
344        wasm_module_hash: &[u8],
345    ) -> InstallChunkedCodeBuilder<'agent, 'canister> {
346        InstallChunkedCodeBuilder::builder(self, *canister_id, wasm_module_hash)
347    }
348
349    /// Install a canister module, automatically selecting one-shot installation or chunked installation depending on module size.
350    ///
351    /// # Warnings
352    ///
353    /// This will clear chunked code storage if chunked installation is used. Do not use with canisters that you are manually uploading chunked code to.
354    pub fn install<'canister: 'builder, 'builder>(
355        &'canister self,
356        canister_id: &Principal,
357        wasm: &'builder [u8],
358    ) -> InstallBuilder<'agent, 'canister, 'builder> {
359        InstallBuilder::builder(self, canister_id, wasm)
360    }
361
362    /// Fetch (query) the logs of a canister.
363    pub fn fetch_canister_logs(
364        &self,
365        args: &FetchCanisterLogsArgs,
366    ) -> impl 'agent + SyncCall<Value = (FetchCanisterLogsResult,)> {
367        self.query(MgmtMethod::FetchCanisterLogs.as_ref())
368            .with_arg(args)
369            .with_effective_canister_id(args.canister_id)
370            .build()
371    }
372
373    /// Creates a canister snapshot, optionally replacing an existing snapshot.
374    ///  
375    /// <div class="warning">Canisters should be stopped before running this method!</div>
376    pub fn take_canister_snapshot(
377        &self,
378        take_args: &TakeCanisterSnapshotArgs,
379    ) -> impl 'agent + AsyncCall<Value = (Snapshot,)> {
380        let args = TakeCanisterSnapshotArgs {
381            sender_canister_version: None,
382            ..take_args.clone()
383        };
384        self.update(MgmtMethod::TakeCanisterSnapshot.as_ref())
385            .with_arg(args)
386            .with_effective_canister_id(take_args.canister_id)
387            .build()
388    }
389
390    /// Loads a canister snapshot by ID, replacing the canister's state with its state at the time the snapshot was taken.
391    ///
392    /// <div class="warning">Canisters should be stopped before running this method!</div>
393    pub fn load_canister_snapshot(
394        &self,
395        load_args: &LoadCanisterSnapshotArgs,
396    ) -> impl 'agent + AsyncCall<Value = ()> {
397        let args = LoadCanisterSnapshotArgs {
398            sender_canister_version: None,
399            ..load_args.clone()
400        };
401        self.update(MgmtMethod::LoadCanisterSnapshot.as_ref())
402            .with_arg(args)
403            .with_effective_canister_id(load_args.canister_id)
404            .build()
405    }
406
407    /// List a canister's recorded snapshots.
408    pub fn list_canister_snapshots(
409        &self,
410        canister_id: &Principal,
411    ) -> impl 'agent + AsyncCall<Value = (Vec<Snapshot>,)> {
412        self.update(MgmtMethod::ListCanisterSnapshots.as_ref())
413            .with_arg(CanisterIdRecord {
414                canister_id: *canister_id,
415            })
416            .with_effective_canister_id(*canister_id)
417            .build()
418    }
419
420    /// Deletes a recorded canister snapshot by ID.
421    pub fn delete_canister_snapshot(
422        &self,
423        delete_args: &DeleteCanisterSnapshotArgs,
424    ) -> impl 'agent + AsyncCall<Value = ()> {
425        self.update(MgmtMethod::DeleteCanisterSnapshot.as_ref())
426            .with_arg(delete_args)
427            .with_effective_canister_id(delete_args.canister_id)
428            .build()
429    }
430
431    /// Reads the metadata of a recorded canister snapshot by canister ID and snapshot ID.
432    pub fn read_canister_snapshot_metadata(
433        &self,
434        metadata_args: &ReadCanisterSnapshotMetadataArgs,
435    ) -> impl 'agent + AsyncCall<Value = (ReadCanisterSnapshotMetadataResult,)> {
436        self.update(MgmtMethod::ReadCanisterSnapshotMetadata.as_ref())
437            .with_arg(metadata_args)
438            .with_effective_canister_id(metadata_args.canister_id)
439            .build()
440    }
441
442    /// Reads the data of a recorded canister snapshot by canister ID and snapshot ID.
443    pub fn read_canister_snapshot_data(
444        &self,
445        data_args: &ReadCanisterSnapshotDataArgs,
446    ) -> impl 'agent + AsyncCall<Value = (ReadCanisterSnapshotDataResult,)> {
447        self.update(MgmtMethod::ReadCanisterSnapshotData.as_ref())
448            .with_arg(data_args)
449            .with_effective_canister_id(data_args.canister_id)
450            .build()
451    }
452
453    /// Uploads the metadata of a canister snapshot by canister ID.
454    pub fn upload_canister_snapshot_metadata(
455        &self,
456        metadata_args: &UploadCanisterSnapshotMetadataArgs,
457    ) -> impl 'agent + AsyncCall<Value = (UploadCanisterSnapshotMetadataResult,)> {
458        self.update(MgmtMethod::UploadCanisterSnapshotMetadata.as_ref())
459            .with_arg(metadata_args)
460            .with_effective_canister_id(metadata_args.canister_id)
461            .build()
462    }
463
464    /// Uploads the data of a canister snapshot by canister ID and snapshot ID..
465    pub fn upload_canister_snapshot_data(
466        &self,
467        data_args: &UploadCanisterSnapshotDataArgs,
468    ) -> impl 'agent + AsyncCall<Value = ()> {
469        self.update(MgmtMethod::UploadCanisterSnapshotData.as_ref())
470            .with_arg(data_args)
471            .with_effective_canister_id(data_args.canister_id)
472            .build()
473    }
474
475    /// Fetch a named metadata section of a canister's Wasm module.
476    pub fn canister_metadata(
477        &self,
478        args: &CanisterMetadataArgs,
479    ) -> impl 'agent + SyncCall<Value = (CanisterMetadataResult,)> {
480        self.query(MgmtMethod::CanisterMetadata.as_ref())
481            .with_arg(args)
482            .with_effective_canister_id(args.canister_id)
483            .build()
484    }
485
486    /// Get a set of metrics for a canister, such as the cycles consumed by different use cases.
487    ///
488    /// Only the canister's controllers or subnet administrators may call this method.
489    ///
490    /// Issued as a (fast, non-replicated) query call by default; call
491    /// [`QueryOrUpdateCall::as_update`] on the returned builder for a replicated
492    /// update call instead.
493    pub fn canister_metrics(
494        &self,
495        canister_id: &Principal,
496    ) -> QueryOrUpdateCall<'agent, '_, (CanisterMetricsResult,)> {
497        QueryOrUpdateCall::new(&self.0, MgmtMethod::CanisterMetrics.into(), *canister_id)
498    }
499
500    /// List all canisters on a subnet, as a set of consecutive canister-id ranges.
501    ///
502    /// Per the IC interface spec, `list_canisters` is a subnet-scoped, query-only
503    /// method that may only be called by external users with subnet administrator
504    /// privileges (it cannot be called by canisters or via replicated calls). The
505    /// request is therefore signed and routed to the subnet-scoped
506    /// `/api/v3/subnet/<id>/query` endpoint rather than issued through the regular
507    /// (canister-scoped) call surface.
508    pub async fn list_canisters(
509        &self,
510        subnet_id: Principal,
511    ) -> Result<ListCanistersResult, AgentError> {
512        let agent = self.0.agent;
513        let signed = agent
514            .query(
515                &Principal::management_canister(),
516                MgmtMethod::ListCanisters.as_ref(),
517            )
518            .with_arg(Encode!().map_err(|e| AgentError::CandidError(Box::new(e)))?)
519            .sign()?;
520        let bytes = agent
521            .query_signed(EffectiveId::Subnet(subnet_id), signed.signed_query)
522            .await?;
523        Decode!(&bytes, ListCanistersResult).map_err(|e| AgentError::CandidError(Box::new(e)))
524    }
525}
526
527/// A management-canister read call that is issued as a (fast, non-replicated)
528/// query call by default, or as a replicated update call via
529/// [`as_update`](Self::as_update).
530///
531/// Query responses come from a single replica. The agent verifies the replica's
532/// signature unless query-signature verification was disabled, so a verified
533/// query response is authenticated by the subnet. Use [`as_update`](Self::as_update)
534/// when a fully replicated, certified response is required, at the cost of a
535/// slower update call.
536///
537/// Returned by [`ManagementCanister::canister_status`] and
538/// [`ManagementCanister::canister_metrics`].
539#[derive(Debug)]
540pub struct QueryOrUpdateCall<'agent, 'canister, Out> {
541    canister: &'canister Canister<'agent>,
542    method_name: &'static str,
543    canister_id: Principal,
544    as_update: bool,
545    _out: PhantomData<Out>,
546}
547
548impl<'agent: 'canister, 'canister, Out> QueryOrUpdateCall<'agent, 'canister, Out>
549where
550    Out: for<'de> ArgumentDecoder<'de> + Send + Sync,
551{
552    fn new(
553        canister: &'canister Canister<'agent>,
554        method_name: &'static str,
555        canister_id: Principal,
556    ) -> Self {
557        Self {
558            canister,
559            method_name,
560            canister_id,
561            as_update: false,
562            _out: PhantomData,
563        }
564    }
565
566    /// Issue this call as a replicated update call instead of the default query.
567    pub fn as_update(mut self) -> Self {
568        self.as_update = true;
569        self
570    }
571
572    /// Perform the call, awaiting the full response: a single query round-trip
573    /// by default, or (after [`as_update`](Self::as_update)) an update call
574    /// followed by polling for its result.
575    pub async fn call(self) -> Result<Out, AgentError> {
576        let arg = CanisterIdRecord {
577            canister_id: self.canister_id,
578        };
579        if self.as_update {
580            self.canister
581                .update(self.method_name)
582                .with_arg(arg)
583                .with_effective_canister_id(self.canister_id)
584                .build()
585                .call_and_wait()
586                .await
587        } else {
588            self.canister
589                .query(self.method_name)
590                .with_arg(arg)
591                .with_effective_canister_id(self.canister_id)
592                .build()
593                .call()
594                .await
595        }
596    }
597}