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::{CandidType, Deserialize, Nat};
10use ic_agent::{export::Principal, Agent};
11use serde::Serialize;
12use std::{convert::AsRef, ops::Deref};
13use strum_macros::{AsRefStr, Display, EnumString};
14
15pub mod attributes;
16pub mod builders;
17
18#[doc(inline)]
19pub use builders::{
20    CreateCanisterBuilder, InstallBuilder, InstallChunkedCodeBuilder, InstallCodeBuilder,
21    UpdateCanisterBuilder,
22};
23
24/// The IC management canister.
25#[derive(Debug, Clone)]
26pub struct ManagementCanister<'agent>(Canister<'agent>);
27
28impl<'agent> Deref for ManagementCanister<'agent> {
29    type Target = Canister<'agent>;
30    fn deref(&self) -> &Self::Target {
31        &self.0
32    }
33}
34
35/// All the known methods of the management canister.
36#[derive(AsRefStr, Debug, EnumString, Display)]
37#[strum(serialize_all = "snake_case")]
38pub enum MgmtMethod {
39    /// See [`ManagementCanister::create_canister`].
40    CreateCanister,
41    /// See [`ManagementCanister::install_code`].
42    InstallCode,
43    /// See [`ManagementCanister::start_canister`].
44    StartCanister,
45    /// See [`ManagementCanister::stop_canister`].
46    StopCanister,
47    /// See [`ManagementCanister::canister_status`].
48    CanisterStatus,
49    /// See [`ManagementCanister::delete_canister`].
50    DeleteCanister,
51    /// See [`ManagementCanister::deposit_cycles`].
52    DepositCycles,
53    /// See [`ManagementCanister::raw_rand`].
54    RawRand,
55    /// See [`CreateCanisterBuilder::as_provisional_create_with_amount`].
56    ProvisionalCreateCanisterWithCycles,
57    /// See [`ManagementCanister::provisional_top_up_canister`].
58    ProvisionalTopUpCanister,
59    /// See [`ManagementCanister::uninstall_code`].
60    UninstallCode,
61    /// See [`ManagementCanister::update_settings`].
62    UpdateSettings,
63    /// See [`ManagementCanister::upload_chunk`].
64    UploadChunk,
65    /// See [`ManagementCanister::clear_chunk_store`].
66    ClearChunkStore,
67    /// See [`ManagementCanister::stored_chunks`].
68    StoredChunks,
69    /// See [`ManagementCanister::install_chunked_code`].
70    InstallChunkedCode,
71    /// See [`ManagementCanister::fetch_canister_logs`].
72    FetchCanisterLogs,
73    /// See [`ManagementCanister::take_canister_snapshot`].
74    TakeCanisterSnapshot,
75    /// See [`ManagementCanister::load_canister_snapshot`].
76    LoadCanisterSnapshot,
77    /// See [`ManagementCanister::list_canister_snapshots`].
78    ListCanisterSnapshots,
79    /// See [`ManagementCanister::delete_canister_snapshot`].
80    DeleteCanisterSnapshot,
81    /// See [`ManagementCanister::read_canister_snapshot_metadata`].
82    ReadCanisterSnapshotMetadata,
83    /// See [`ManagementCanister::read_canister_snapshot_data`].
84    ReadCanisterSnapshotData,
85    /// See [`ManagementCanister::upload_canister_snapshot_metadata`].
86    UploadCanisterSnapshotMetadata,
87    /// See [`ManagementCanister::upload_canister_snapshot_data`].
88    UploadCanisterSnapshotData,
89    /// There is no corresponding agent function as only canisters can call it.
90    EcdsaPublicKey,
91    /// There is no corresponding agent function as only canisters can call it.
92    SignWithEcdsa,
93    /// There is no corresponding agent function as only canisters can call it. Use [`BitcoinCanister`](super::BitcoinCanister) instead.
94    BitcoinGetBalance,
95    /// There is no corresponding agent function as only canisters can call it. Use [`BitcoinCanister`](super::BitcoinCanister) instead.
96    BitcoinGetUtxos,
97    /// There is no corresponding agent function as only canisters can call it. Use [`BitcoinCanister`](super::BitcoinCanister) instead.
98    BitcoinSendTransaction,
99    /// There is no corresponding agent function as only canisters can call it. Use [`BitcoinCanister`](super::BitcoinCanister) instead.
100    BitcoinGetCurrentFeePercentiles,
101    /// There is no corresponding agent function as only canisters can call it. Use [`BitcoinCanister`](super::BitcoinCanister) instead.
102    BitcoinGetBlockHeaders,
103    /// There is no corresponding agent function as only canisters can call it.
104    NodeMetricsHistory,
105    /// There is no corresponding agent function as only canisters can call it.
106    CanisterInfo,
107}
108
109impl<'agent> ManagementCanister<'agent> {
110    /// Create an instance of a `ManagementCanister` interface pointing to the specified Canister ID.
111    pub fn create(agent: &'agent Agent) -> Self {
112        Self(
113            Canister::builder()
114                .with_agent(agent)
115                .with_canister_id(Principal::management_canister())
116                .build()
117                .unwrap(),
118        )
119    }
120
121    /// Create a `ManagementCanister` interface from an existing canister object.
122    pub fn from_canister(canister: Canister<'agent>) -> Self {
123        Self(canister)
124    }
125}
126
127/// The complete canister status information of a canister. This includes
128/// the `CanisterStatus`, a hash of the module installed on the canister (None if nothing installed),
129/// the controller of the canister, the canister's memory size, and its balance in cycles.
130#[derive(Clone, Debug, Deserialize, CandidType)]
131pub struct StatusCallResult {
132    /// The status of the canister.
133    pub status: CanisterStatus,
134    /// The canister's settings.
135    pub settings: DefiniteCanisterSettings,
136    /// The SHA-256 hash of the canister's installed code, if any.
137    pub module_hash: Option<Vec<u8>>,
138    /// The total size, in bytes, of the memory the canister is using.
139    pub memory_size: Nat,
140    /// The canister's cycle balance.
141    pub cycles: Nat,
142    /// The canister's reserved cycles balance.
143    pub reserved_cycles: Nat,
144    /// The cycles burned by the canister in one day for its resource usage
145    /// (compute and memory allocation and memory usage).
146    pub idle_cycles_burned_per_day: Nat,
147    /// Additional information relating to query calls.
148    pub query_stats: QueryStats,
149}
150
151/// Statistics relating to query calls.
152#[derive(Clone, Debug, Deserialize, CandidType)]
153pub struct QueryStats {
154    /// The total number of query calls this canister has performed.
155    pub num_calls_total: Nat,
156    /// The total number of instructions this canister has executed during query calls.
157    pub num_instructions_total: Nat,
158    /// The total number of bytes in request payloads sent to this canister's query calls.
159    pub request_payload_bytes_total: Nat,
160    /// The total number of bytes in response payloads returned from this canister's query calls.
161    pub response_payload_bytes_total: Nat,
162}
163
164/// Log visibility for a canister.
165#[derive(Default, Clone, CandidType, Deserialize, Debug, PartialEq, Eq)]
166pub enum LogVisibility {
167    #[default]
168    #[serde(rename = "controllers")]
169    /// Canister logs are visible to controllers only.
170    Controllers,
171    #[serde(rename = "public")]
172    /// Canister logs are visible to everyone.
173    Public,
174    #[serde(rename = "allowed_viewers")]
175    /// Canister logs are visible to a set of principals.
176    AllowedViewers(Vec<Principal>),
177}
178
179/// The concrete settings of a canister.
180#[derive(Clone, Debug, Deserialize, CandidType)]
181pub struct DefiniteCanisterSettings {
182    /// The set of canister controllers. Controllers can update the canister via the management canister.
183    pub controllers: Vec<Principal>,
184    /// The allocation percentage (between 0 and 100 inclusive) for *guaranteed* compute capacity.
185    pub compute_allocation: Nat,
186    /// The allocation, in bytes (up to 256 TiB) that the canister is allowed to use for storage.
187    pub memory_allocation: Nat,
188    /// The IC will freeze a canister protectively if it will likely run out of cycles before this amount of time,
189    /// in seconds (up to `u64::MAX`), has passed.
190    pub freezing_threshold: Nat,
191    /// The upper limit of the canister's reserved cycles balance.
192    pub reserved_cycles_limit: Option<Nat>,
193    /// A soft limit on the Wasm memory usage of the canister in bytes (up to 256TiB).
194    pub wasm_memory_limit: Option<Nat>,
195    /// A threshold on the Wasm memory usage of the canister as a distance in bytes from `wasm_memory_limit`,
196    /// at which the canister's `on_low_wasm_memory` hook will be called (up to 256TiB)
197    pub wasm_memory_threshold: Option<Nat>,
198    /// The canister log visibility. Defines which principals are allowed to fetch logs.
199    pub log_visibility: LogVisibility,
200}
201
202impl std::fmt::Display for StatusCallResult {
203    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
204        std::fmt::Debug::fmt(self, f)
205    }
206}
207
208/// The status of a Canister, whether it's running, in the process of stopping, or
209/// stopped.
210#[derive(Clone, Debug, Deserialize, Eq, PartialEq, CandidType)]
211pub enum CanisterStatus {
212    /// The canister is currently running.
213    #[serde(rename = "running")]
214    Running,
215    /// The canister is in the process of stopping.
216    #[serde(rename = "stopping")]
217    Stopping,
218    /// The canister is stopped.
219    #[serde(rename = "stopped")]
220    Stopped,
221}
222
223impl std::fmt::Display for CanisterStatus {
224    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
225        std::fmt::Debug::fmt(self, f)
226    }
227}
228
229/// A log record of a canister.
230#[derive(Default, Clone, CandidType, Deserialize, Debug, PartialEq, Eq)]
231pub struct CanisterLogRecord {
232    /// The index of the log record.
233    pub idx: u64,
234    /// The timestamp of the log record.
235    pub timestamp_nanos: u64,
236    /// The content of the log record.
237    #[serde(with = "serde_bytes")]
238    pub content: Vec<u8>,
239}
240
241/// The result of a [`ManagementCanister::fetch_canister_logs`] call.
242#[derive(Clone, Debug, Deserialize, Eq, PartialEq, CandidType)]
243pub struct FetchCanisterLogsResponse {
244    /// The logs of the canister.
245    pub canister_log_records: Vec<CanisterLogRecord>,
246}
247
248/// Chunk hash.
249#[derive(
250    CandidType, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Serialize,
251)]
252pub struct ChunkHash {
253    /// The hash of an uploaded chunk
254    #[serde(with = "serde_bytes")]
255    pub hash: Vec<u8>,
256}
257
258/// Return type of [`ManagementCanister::stored_chunks`].
259pub type StoreChunksResult = Vec<ChunkHash>;
260
261/// Return type of [`ManagementCanister::upload_chunk`].
262pub type UploadChunkResult = ChunkHash;
263
264/// A recorded snapshot of a canister. Can be restored with [`ManagementCanister::load_canister_snapshot`].
265#[derive(Debug, Clone, CandidType, Deserialize)]
266pub struct Snapshot {
267    /// The ID of the snapshot.
268    #[serde(with = "serde_bytes")]
269    pub id: Vec<u8>,
270    /// The Unix nanosecond timestamp the snapshot was taken at.
271    pub taken_at_timestamp: u64,
272    /// The size of the snapshot in bytes.
273    pub total_size: u64,
274}
275
276/// The source of a snapshot.
277#[derive(Debug, Clone, CandidType, Deserialize, Serialize)]
278pub enum SnapshotSource {
279    /// The snapshot was taken from a canister.
280    #[serde(rename = "taken_from_canister")]
281    TakenFromCanister,
282    /// The snapshot was created by uploading metadata.
283    #[serde(rename = "metadata_upload")]
284    MetadataUpload,
285}
286
287/// An exported global variable.
288#[derive(Debug, Clone, CandidType, Deserialize, Serialize)]
289pub enum ExportedGlobal {
290    /// A 32-bit integer.
291    #[serde(rename = "i32")]
292    I32(i32),
293    /// A 64-bit integer.
294    #[serde(rename = "i64")]
295    I64(i64),
296    /// A 32-bit floating point number.
297    #[serde(rename = "f32")]
298    F32(f32),
299    /// A 64-bit floating point number.
300    #[serde(rename = "f64")]
301    F64(f64),
302    /// A 128-bit integer.
303    #[serde(rename = "v128")]
304    V128(Nat),
305}
306
307/// The status of a global timer.
308#[derive(Debug, Clone, CandidType, Deserialize, Serialize)]
309pub enum CanisterTimer {
310    /// The global timer is inactive.
311    #[serde(rename = "inactive")]
312    Inactive,
313    /// The global timer is active.
314    #[serde(rename = "active")]
315    Active(u64),
316}
317
318/// The status of a low wasm memory hook.
319#[derive(Debug, Clone, CandidType, Deserialize, Serialize)]
320pub enum OnLowWasmMemoryHookStatus {
321    /// The condition for the  low wasm memory hook is not satisfied.
322    #[serde(rename = "condition_not_satisfied")]
323    ConditionNotSatisfied,
324    /// The low wasm memory hook is ready to be executed.
325    #[serde(rename = "ready")]
326    Ready,
327    /// The low wasm memory hook has been executed.
328    #[serde(rename = "executed")]
329    Executed,
330}
331
332/// Return type of [`ManagementCanister::read_canister_snapshot_metadata`].
333#[derive(Debug, Clone, CandidType, Deserialize, Serialize)]
334pub struct SnapshotMetadata {
335    /// The source of the snapshot.
336    pub source: SnapshotSource,
337    /// The Unix nanosecond timestamp the snapshot was taken at.
338    pub taken_at_timestamp: u64,
339    /// The size of the Wasm module.
340    pub wasm_module_size: u64,
341    /// The exported globals.
342    pub exported_globals: Vec<ExportedGlobal>,
343    /// The size of the Wasm memory.
344    pub wasm_memory_size: u64,
345    /// The size of the stable memory.
346    pub stable_memory_size: u64,
347    /// The chunk store of the Wasm module.
348    pub wasm_chunk_store: StoreChunksResult,
349    /// The version of the canister.
350    pub canister_version: u64,
351    /// The certified data.
352    #[serde(with = "serde_bytes")]
353    pub certified_data: Vec<u8>,
354    /// The status of the global timer.
355    pub global_timer: Option<CanisterTimer>,
356    /// The status of the low wasm memory hook.
357    pub on_low_wasm_memory_hook_status: Option<OnLowWasmMemoryHookStatus>,
358}
359
360/// Snapshot data kind.
361#[derive(Debug, Clone, CandidType, Deserialize, Serialize)]
362pub enum SnapshotDataKind {
363    /// Wasm module.
364    #[serde(rename = "wasm_module")]
365    WasmModule {
366        /// Offset in bytes.
367        offset: u64,
368        /// Size of the data in bytes.
369        size: u64,
370    },
371    /// Main memory.
372    #[serde(rename = "main_memory")]
373    MainMemory {
374        /// Offset in bytes.
375        offset: u64,
376        /// Size of the data in bytes.
377        size: u64,
378    },
379    /// Stable memory.
380    #[serde(rename = "stable_memory")]
381    StableMemory {
382        /// Offset in bytes.
383        offset: u64,
384        /// Size of the data in bytes.
385        size: u64,
386    },
387    /// Chunk hash.
388    #[serde(rename = "wasm_chunk")]
389    WasmChunk {
390        /// The hash of the chunk.
391        #[serde(with = "serde_bytes")]
392        hash: Vec<u8>,
393    },
394}
395
396/// Snapshot reading result.
397#[derive(Debug, Clone, CandidType, Deserialize, Serialize)]
398pub struct SnapshotDataResult {
399    /// The returned chunk of data.
400    #[serde(with = "serde_bytes")]
401    pub chunk: Vec<u8>,
402}
403
404/// The ID of a snapshot.
405#[derive(Debug, Clone, CandidType, Deserialize, Serialize)]
406pub struct CanisterSnapshotId {
407    /// The ID of the snapshot.
408    #[serde(with = "serde_bytes")]
409    pub snapshot_id: Vec<u8>,
410}
411
412/// Snapshot data offset.
413#[derive(Debug, Clone, CandidType, Deserialize, Serialize)]
414pub enum SnapshotDataOffset {
415    /// Wasm module.
416    #[serde(rename = "wasm_module")]
417    WasmModule {
418        /// Offset in bytes.
419        offset: u64,
420    },
421    /// Main memory.
422    #[serde(rename = "main_memory")]
423    MainMemory {
424        /// Offset in bytes.
425        offset: u64,
426    },
427    /// Stable memory.
428    #[serde(rename = "stable_memory")]
429    StableMemory {
430        /// Offset in bytes.
431        offset: u64,
432    },
433    /// Wasm chunk.
434    #[serde(rename = "wasm_chunk")]
435    WasmChunk,
436}
437
438impl<'agent> ManagementCanister<'agent> {
439    /// Get the status of a canister.
440    pub fn canister_status(
441        &self,
442        canister_id: &Principal,
443    ) -> impl 'agent + AsyncCall<Value = (StatusCallResult,)> {
444        #[derive(CandidType)]
445        struct In {
446            canister_id: Principal,
447        }
448
449        self.update(MgmtMethod::CanisterStatus.as_ref())
450            .with_arg(In {
451                canister_id: *canister_id,
452            })
453            .with_effective_canister_id(canister_id.to_owned())
454            .build()
455            .map(|result: (StatusCallResult,)| (result.0,))
456    }
457
458    /// Create a canister.
459    pub fn create_canister<'canister>(&'canister self) -> CreateCanisterBuilder<'agent, 'canister> {
460        CreateCanisterBuilder::builder(self)
461    }
462
463    /// This method deposits the cycles included in this call into the specified canister.
464    /// Only the controller of the canister can deposit cycles.
465    pub fn deposit_cycles(&self, canister_id: &Principal) -> impl 'agent + AsyncCall<Value = ()> {
466        #[derive(CandidType)]
467        struct Argument {
468            canister_id: Principal,
469        }
470
471        self.update(MgmtMethod::DepositCycles.as_ref())
472            .with_arg(Argument {
473                canister_id: *canister_id,
474            })
475            .with_effective_canister_id(canister_id.to_owned())
476            .build()
477    }
478
479    /// Deletes a canister.
480    pub fn delete_canister(&self, canister_id: &Principal) -> impl 'agent + AsyncCall<Value = ()> {
481        #[derive(CandidType)]
482        struct Argument {
483            canister_id: Principal,
484        }
485
486        self.update(MgmtMethod::DeleteCanister.as_ref())
487            .with_arg(Argument {
488                canister_id: *canister_id,
489            })
490            .with_effective_canister_id(canister_id.to_owned())
491            .build()
492    }
493
494    /// Until developers can convert real ICP tokens to a top up an existing canister,
495    /// the system provides the `provisional_top_up_canister` method.
496    /// It adds amount cycles to the balance of canister identified by amount
497    /// (implicitly capping it at `MAX_CANISTER_BALANCE`).
498    pub fn provisional_top_up_canister(
499        &self,
500        canister_id: &Principal,
501        amount: u64,
502    ) -> impl 'agent + AsyncCall<Value = ()> {
503        #[derive(CandidType)]
504        struct Argument {
505            canister_id: Principal,
506            amount: u64,
507        }
508
509        self.update(MgmtMethod::ProvisionalTopUpCanister.as_ref())
510            .with_arg(Argument {
511                canister_id: *canister_id,
512                amount,
513            })
514            .with_effective_canister_id(canister_id.to_owned())
515            .build()
516    }
517
518    /// This method takes no input and returns 32 pseudo-random bytes to the caller.
519    /// The return value is unknown to any part of the IC at time of the submission of this call.
520    /// A new return value is generated for each call to this method.
521    pub fn raw_rand(&self) -> impl 'agent + AsyncCall<Value = (Vec<u8>,)> {
522        self.update(MgmtMethod::RawRand.as_ref())
523            .build()
524            .map(|result: (Vec<u8>,)| (result.0,))
525    }
526
527    /// Starts a canister.
528    pub fn start_canister(&self, canister_id: &Principal) -> impl 'agent + AsyncCall<Value = ()> {
529        #[derive(CandidType)]
530        struct Argument {
531            canister_id: Principal,
532        }
533
534        self.update(MgmtMethod::StartCanister.as_ref())
535            .with_arg(Argument {
536                canister_id: *canister_id,
537            })
538            .with_effective_canister_id(canister_id.to_owned())
539            .build()
540    }
541
542    /// Stop a canister.
543    pub fn stop_canister(&self, canister_id: &Principal) -> impl 'agent + AsyncCall<Value = ()> {
544        #[derive(CandidType)]
545        struct Argument {
546            canister_id: Principal,
547        }
548
549        self.update(MgmtMethod::StopCanister.as_ref())
550            .with_arg(Argument {
551                canister_id: *canister_id,
552            })
553            .with_effective_canister_id(canister_id.to_owned())
554            .build()
555    }
556
557    /// This method removes a canister’s code and state, making the canister empty again.
558    /// Only the controller of the canister can uninstall code.
559    /// Uninstalling a canister’s code will reject all calls that the canister has not yet responded to,
560    /// and drop the canister’s code and state.
561    /// Outstanding responses to the canister will not be processed, even if they arrive after code has been installed again.
562    /// The canister is now empty. In particular, any incoming or queued calls will be rejected.
563    //// A canister after uninstalling retains its cycles balance, controller, status, and allocations.
564    pub fn uninstall_code(&self, canister_id: &Principal) -> impl 'agent + AsyncCall<Value = ()> {
565        #[derive(CandidType)]
566        struct Argument {
567            canister_id: Principal,
568        }
569
570        self.update(MgmtMethod::UninstallCode.as_ref())
571            .with_arg(Argument {
572                canister_id: *canister_id,
573            })
574            .with_effective_canister_id(canister_id.to_owned())
575            .build()
576    }
577
578    /// Install a canister, with all the arguments necessary for creating the canister.
579    pub fn install_code<'canister>(
580        &'canister self,
581        canister_id: &Principal,
582        wasm: &'canister [u8],
583    ) -> InstallCodeBuilder<'agent, 'canister> {
584        InstallCodeBuilder::builder(self, canister_id, wasm)
585    }
586
587    /// Update one or more of a canisters settings (i.e its controller, compute allocation, or memory allocation.)
588    pub fn update_settings<'canister>(
589        &'canister self,
590        canister_id: &Principal,
591    ) -> UpdateCanisterBuilder<'agent, 'canister> {
592        UpdateCanisterBuilder::builder(self, canister_id)
593    }
594
595    /// Upload a chunk of a WASM module to a canister's chunked WASM storage.
596    pub fn upload_chunk(
597        &self,
598        canister_id: &Principal,
599        chunk: &[u8],
600    ) -> impl 'agent + AsyncCall<Value = (UploadChunkResult,)> {
601        #[derive(CandidType, Deserialize)]
602        struct Argument<'a> {
603            canister_id: Principal,
604            #[serde(with = "serde_bytes")]
605            chunk: &'a [u8],
606        }
607
608        self.update(MgmtMethod::UploadChunk.as_ref())
609            .with_arg(Argument {
610                canister_id: *canister_id,
611                chunk,
612            })
613            .with_effective_canister_id(*canister_id)
614            .build()
615    }
616
617    /// Clear a canister's chunked WASM storage.
618    pub fn clear_chunk_store(
619        &self,
620        canister_id: &Principal,
621    ) -> impl 'agent + AsyncCall<Value = ()> {
622        #[derive(CandidType)]
623        struct Argument<'a> {
624            canister_id: &'a Principal,
625        }
626        self.update(MgmtMethod::ClearChunkStore.as_ref())
627            .with_arg(Argument { canister_id })
628            .with_effective_canister_id(*canister_id)
629            .build()
630    }
631
632    /// Get a list of the hashes of a canister's stored WASM chunks
633    pub fn stored_chunks(
634        &self,
635        canister_id: &Principal,
636    ) -> impl 'agent + AsyncCall<Value = (StoreChunksResult,)> {
637        #[derive(CandidType)]
638        struct Argument<'a> {
639            canister_id: &'a Principal,
640        }
641        self.update(MgmtMethod::StoredChunks.as_ref())
642            .with_arg(Argument { canister_id })
643            .with_effective_canister_id(*canister_id)
644            .build()
645    }
646
647    /// Install a canister module previously uploaded in chunks via [`upload_chunk`](Self::upload_chunk).
648    pub fn install_chunked_code<'canister>(
649        &'canister self,
650        canister_id: &Principal,
651        wasm_module_hash: &[u8],
652    ) -> InstallChunkedCodeBuilder<'agent, 'canister> {
653        InstallChunkedCodeBuilder::builder(self, *canister_id, wasm_module_hash)
654    }
655
656    /// Install a canister module, automatically selecting one-shot installation or chunked installation depending on module size.
657    ///
658    /// # Warnings
659    ///
660    /// This will clear chunked code storage if chunked installation is used. Do not use with canisters that you are manually uploading chunked code to.
661    pub fn install<'canister: 'builder, 'builder>(
662        &'canister self,
663        canister_id: &Principal,
664        wasm: &'builder [u8],
665    ) -> InstallBuilder<'agent, 'canister, 'builder> {
666        InstallBuilder::builder(self, canister_id, wasm)
667    }
668
669    /// Fetch the logs of a canister.
670    pub fn fetch_canister_logs(
671        &self,
672        canister_id: &Principal,
673    ) -> impl 'agent + SyncCall<Value = (FetchCanisterLogsResponse,)> {
674        #[derive(CandidType)]
675        struct In {
676            canister_id: Principal,
677        }
678
679        // `fetch_canister_logs` is only supported in non-replicated mode.
680        self.query(MgmtMethod::FetchCanisterLogs.as_ref())
681            .with_arg(In {
682                canister_id: *canister_id,
683            })
684            .with_effective_canister_id(*canister_id)
685            .build()
686    }
687
688    /// Creates a canister snapshot, optionally replacing an existing snapshot.
689    ///  
690    /// <div class="warning">Canisters should be stopped before running this method!</div>
691    pub fn take_canister_snapshot(
692        &self,
693        canister_id: &Principal,
694        replace_snapshot: Option<&[u8]>,
695    ) -> impl 'agent + AsyncCall<Value = (Snapshot,)> {
696        #[derive(CandidType)]
697        struct In<'a> {
698            canister_id: Principal,
699            replace_snapshot: Option<&'a [u8]>,
700        }
701        self.update(MgmtMethod::TakeCanisterSnapshot.as_ref())
702            .with_arg(In {
703                canister_id: *canister_id,
704                replace_snapshot,
705            })
706            .with_effective_canister_id(*canister_id)
707            .build()
708    }
709
710    /// Loads a canister snapshot by ID, replacing the canister's state with its state at the time the snapshot was taken.
711    ///
712    /// <div class="warning">Canisters should be stopped before running this method!</div>
713    pub fn load_canister_snapshot(
714        &self,
715        canister_id: &Principal,
716        snapshot_id: &[u8],
717    ) -> impl 'agent + AsyncCall<Value = ()> {
718        #[derive(CandidType)]
719        struct In<'a> {
720            canister_id: Principal,
721            snapshot_id: &'a [u8],
722            sender_canister_version: Option<u64>,
723        }
724        self.update(MgmtMethod::LoadCanisterSnapshot.as_ref())
725            .with_arg(In {
726                canister_id: *canister_id,
727                snapshot_id,
728                sender_canister_version: None,
729            })
730            .with_effective_canister_id(*canister_id)
731            .build()
732    }
733
734    /// List a canister's recorded snapshots.
735    pub fn list_canister_snapshots(
736        &self,
737        canister_id: &Principal,
738    ) -> impl 'agent + AsyncCall<Value = (Vec<Snapshot>,)> {
739        #[derive(CandidType)]
740        struct In {
741            canister_id: Principal,
742        }
743        self.update(MgmtMethod::ListCanisterSnapshots.as_ref())
744            .with_arg(In {
745                canister_id: *canister_id,
746            })
747            .with_effective_canister_id(*canister_id)
748            .build()
749    }
750
751    /// Deletes a recorded canister snapshot by ID.
752    pub fn delete_canister_snapshot(
753        &self,
754        canister_id: &Principal,
755        snapshot_id: &[u8],
756    ) -> impl 'agent + AsyncCall<Value = ()> {
757        #[derive(CandidType)]
758        struct In<'a> {
759            canister_id: Principal,
760            snapshot_id: &'a [u8],
761        }
762        self.update(MgmtMethod::DeleteCanisterSnapshot.as_ref())
763            .with_arg(In {
764                canister_id: *canister_id,
765                snapshot_id,
766            })
767            .build()
768    }
769
770    /// Reads the metadata of a recorded canister snapshot by canister ID and snapshot ID.
771    pub fn read_canister_snapshot_metadata(
772        &self,
773        canister_id: &Principal,
774        snapshot_id: &[u8],
775    ) -> impl 'agent + AsyncCall<Value = (SnapshotMetadata,)> {
776        #[derive(CandidType)]
777        struct In<'a> {
778            canister_id: Principal,
779            snapshot_id: &'a [u8],
780        }
781        self.update(MgmtMethod::ReadCanisterSnapshotMetadata.as_ref())
782            .with_arg(In {
783                canister_id: *canister_id,
784                snapshot_id,
785            })
786            .with_effective_canister_id(*canister_id)
787            .build()
788    }
789
790    /// Reads the data of a recorded canister snapshot by canister ID and snapshot ID.
791    pub fn read_canister_snapshot_data(
792        &self,
793        canister_id: &Principal,
794        snapshot_id: &[u8],
795        kind: &SnapshotDataKind,
796    ) -> impl 'agent + AsyncCall<Value = (SnapshotDataResult,)> {
797        #[derive(CandidType)]
798        struct In<'a> {
799            canister_id: Principal,
800            snapshot_id: &'a [u8],
801            kind: &'a SnapshotDataKind,
802        }
803        self.update(MgmtMethod::ReadCanisterSnapshotData.as_ref())
804            .with_arg(In {
805                canister_id: *canister_id,
806                snapshot_id,
807                kind,
808            })
809            .with_effective_canister_id(*canister_id)
810            .build()
811    }
812
813    /// Uploads the metadata of a canister snapshot by canister ID.
814    pub fn upload_canister_snapshot_metadata(
815        &self,
816        canister_id: &Principal,
817        replace_snapshot: Option<&[u8]>,
818        metadata: &SnapshotMetadata,
819    ) -> impl 'agent + AsyncCall<Value = (CanisterSnapshotId,)> {
820        #[derive(CandidType)]
821        struct In<'a> {
822            canister_id: Principal,
823            replace_snapshot: Option<&'a [u8]>,
824            wasm_module_size: u64,
825            exported_globals: &'a Vec<ExportedGlobal>,
826            wasm_memory_size: u64,
827            stable_memory_size: u64,
828            certified_data: &'a Vec<u8>,
829            global_timer: Option<&'a CanisterTimer>,
830            on_low_wasm_memory_hook_status: Option<&'a OnLowWasmMemoryHookStatus>,
831        }
832        self.update(MgmtMethod::UploadCanisterSnapshotMetadata.as_ref())
833            .with_arg(In {
834                canister_id: *canister_id,
835                replace_snapshot,
836                wasm_module_size: metadata.wasm_module_size,
837                exported_globals: &metadata.exported_globals,
838                wasm_memory_size: metadata.wasm_memory_size,
839                stable_memory_size: metadata.stable_memory_size,
840                certified_data: &metadata.certified_data,
841                global_timer: metadata.global_timer.as_ref(),
842                on_low_wasm_memory_hook_status: metadata.on_low_wasm_memory_hook_status.as_ref(),
843            })
844            .with_effective_canister_id(*canister_id)
845            .build()
846    }
847
848    /// Uploads the data of a canister snapshot by canister ID and snapshot ID..
849    pub fn upload_canister_snapshot_data(
850        &self,
851        canister_id: &Principal,
852        snapshot_id: &[u8],
853        kind: &SnapshotDataOffset,
854        chunk: &[u8],
855    ) -> impl 'agent + AsyncCall<Value = ()> {
856        #[derive(CandidType)]
857        struct In<'a> {
858            canister_id: Principal,
859            snapshot_id: &'a [u8],
860            kind: &'a SnapshotDataOffset,
861            chunk: &'a [u8],
862        }
863        self.update(MgmtMethod::UploadCanisterSnapshotData.as_ref())
864            .with_arg(In {
865                canister_id: *canister_id,
866                snapshot_id,
867                kind,
868                chunk,
869            })
870            .with_effective_canister_id(*canister_id)
871            .build()
872    }
873}