1use crate::{
6 call::{AsyncCall, SyncCall},
7 Canister,
8};
9use candid::{CandidType, Deserialize, Nat};
10use ic_agent::{export::Principal, Agent};
11use std::{convert::AsRef, ops::Deref};
12use strum_macros::{AsRefStr, Display, EnumString};
13
14pub mod attributes;
15pub mod builders;
16
17#[doc(inline)]
18pub use builders::{
19 CreateCanisterBuilder, InstallBuilder, InstallChunkedCodeBuilder, InstallCodeBuilder,
20 UpdateCanisterBuilder,
21};
22
23#[derive(Debug, Clone)]
25pub struct ManagementCanister<'agent>(Canister<'agent>);
26
27impl<'agent> Deref for ManagementCanister<'agent> {
28 type Target = Canister<'agent>;
29 fn deref(&self) -> &Self::Target {
30 &self.0
31 }
32}
33
34#[derive(AsRefStr, Debug, EnumString, Display)]
36#[strum(serialize_all = "snake_case")]
37pub enum MgmtMethod {
38 CreateCanister,
40 InstallCode,
42 StartCanister,
44 StopCanister,
46 CanisterStatus,
48 DeleteCanister,
50 DepositCycles,
52 RawRand,
54 ProvisionalCreateCanisterWithCycles,
56 ProvisionalTopUpCanister,
58 UninstallCode,
60 UpdateSettings,
62 UploadChunk,
64 ClearChunkStore,
66 StoredChunks,
68 InstallChunkedCode,
70 FetchCanisterLogs,
72 TakeCanisterSnapshot,
74 LoadCanisterSnapshot,
76 ListCanisterSnapshots,
78 DeleteCanisterSnapshot,
80 EcdsaPublicKey,
82 SignWithEcdsa,
84 BitcoinGetBalance,
86 BitcoinGetUtxos,
88 BitcoinSendTransaction,
90 BitcoinGetCurrentFeePercentiles,
92 BitcoinGetBlockHeaders,
94 NodeMetricsHistory,
96 CanisterInfo,
98}
99
100impl<'agent> ManagementCanister<'agent> {
101 pub fn create(agent: &'agent Agent) -> Self {
103 Self(
104 Canister::builder()
105 .with_agent(agent)
106 .with_canister_id(Principal::management_canister())
107 .build()
108 .unwrap(),
109 )
110 }
111
112 pub fn from_canister(canister: Canister<'agent>) -> Self {
114 Self(canister)
115 }
116}
117
118#[derive(Clone, Debug, Deserialize, CandidType)]
122pub struct StatusCallResult {
123 pub status: CanisterStatus,
125 pub settings: DefiniteCanisterSettings,
127 pub module_hash: Option<Vec<u8>>,
129 pub memory_size: Nat,
131 pub cycles: Nat,
133 pub reserved_cycles: Nat,
135 pub idle_cycles_burned_per_day: Nat,
138 pub query_stats: QueryStats,
140}
141
142#[derive(Clone, Debug, Deserialize, CandidType)]
144pub struct QueryStats {
145 pub num_calls_total: Nat,
147 pub num_instructions_total: Nat,
149 pub request_payload_bytes_total: Nat,
151 pub response_payload_bytes_total: Nat,
153}
154
155#[derive(Default, Clone, CandidType, Deserialize, Debug, PartialEq, Eq)]
157pub enum LogVisibility {
158 #[default]
159 #[serde(rename = "controllers")]
160 Controllers,
162 #[serde(rename = "public")]
163 Public,
165 #[serde(rename = "allowed_viewers")]
166 AllowedViewers(Vec<Principal>),
168}
169
170#[derive(Clone, Debug, Deserialize, CandidType)]
172pub struct DefiniteCanisterSettings {
173 pub controllers: Vec<Principal>,
175 pub compute_allocation: Nat,
177 pub memory_allocation: Nat,
179 pub freezing_threshold: Nat,
182 pub reserved_cycles_limit: Option<Nat>,
184 pub wasm_memory_limit: Option<Nat>,
186 pub wasm_memory_threshold: Option<Nat>,
189 pub log_visibility: LogVisibility,
191}
192
193impl std::fmt::Display for StatusCallResult {
194 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
195 std::fmt::Debug::fmt(self, f)
196 }
197}
198
199#[derive(Clone, Debug, Deserialize, Eq, PartialEq, CandidType)]
202pub enum CanisterStatus {
203 #[serde(rename = "running")]
205 Running,
206 #[serde(rename = "stopping")]
208 Stopping,
209 #[serde(rename = "stopped")]
211 Stopped,
212}
213
214impl std::fmt::Display for CanisterStatus {
215 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
216 std::fmt::Debug::fmt(self, f)
217 }
218}
219
220#[derive(Default, Clone, CandidType, Deserialize, Debug, PartialEq, Eq)]
222pub struct CanisterLogRecord {
223 pub idx: u64,
225 pub timestamp_nanos: u64,
227 #[serde(with = "serde_bytes")]
229 pub content: Vec<u8>,
230}
231
232#[derive(Clone, Debug, Deserialize, Eq, PartialEq, CandidType)]
234pub struct FetchCanisterLogsResponse {
235 pub canister_log_records: Vec<CanisterLogRecord>,
237}
238
239#[derive(CandidType, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
241pub struct ChunkHash {
242 #[serde(with = "serde_bytes")]
244 pub hash: Vec<u8>,
245}
246
247pub type StoreChunksResult = Vec<ChunkHash>;
249
250pub type UploadChunkResult = ChunkHash;
252
253#[derive(Debug, Clone, CandidType, Deserialize)]
255pub struct Snapshot {
256 #[serde(with = "serde_bytes")]
258 pub id: Vec<u8>,
259 pub taken_at_timestamp: u64,
261 pub total_size: u64,
263}
264
265impl<'agent> ManagementCanister<'agent> {
266 pub fn canister_status(
268 &self,
269 canister_id: &Principal,
270 ) -> impl 'agent + AsyncCall<Value = (StatusCallResult,)> {
271 #[derive(CandidType)]
272 struct In {
273 canister_id: Principal,
274 }
275
276 self.update(MgmtMethod::CanisterStatus.as_ref())
277 .with_arg(In {
278 canister_id: *canister_id,
279 })
280 .with_effective_canister_id(canister_id.to_owned())
281 .build()
282 .map(|result: (StatusCallResult,)| (result.0,))
283 }
284
285 pub fn create_canister<'canister>(&'canister self) -> CreateCanisterBuilder<'agent, 'canister> {
287 CreateCanisterBuilder::builder(self)
288 }
289
290 pub fn deposit_cycles(&self, canister_id: &Principal) -> impl 'agent + AsyncCall<Value = ()> {
293 #[derive(CandidType)]
294 struct Argument {
295 canister_id: Principal,
296 }
297
298 self.update(MgmtMethod::DepositCycles.as_ref())
299 .with_arg(Argument {
300 canister_id: *canister_id,
301 })
302 .with_effective_canister_id(canister_id.to_owned())
303 .build()
304 }
305
306 pub fn delete_canister(&self, canister_id: &Principal) -> impl 'agent + AsyncCall<Value = ()> {
308 #[derive(CandidType)]
309 struct Argument {
310 canister_id: Principal,
311 }
312
313 self.update(MgmtMethod::DeleteCanister.as_ref())
314 .with_arg(Argument {
315 canister_id: *canister_id,
316 })
317 .with_effective_canister_id(canister_id.to_owned())
318 .build()
319 }
320
321 pub fn provisional_top_up_canister(
326 &self,
327 canister_id: &Principal,
328 amount: u64,
329 ) -> impl 'agent + AsyncCall<Value = ()> {
330 #[derive(CandidType)]
331 struct Argument {
332 canister_id: Principal,
333 amount: u64,
334 }
335
336 self.update(MgmtMethod::ProvisionalTopUpCanister.as_ref())
337 .with_arg(Argument {
338 canister_id: *canister_id,
339 amount,
340 })
341 .with_effective_canister_id(canister_id.to_owned())
342 .build()
343 }
344
345 pub fn raw_rand(&self) -> impl 'agent + AsyncCall<Value = (Vec<u8>,)> {
349 self.update(MgmtMethod::RawRand.as_ref())
350 .build()
351 .map(|result: (Vec<u8>,)| (result.0,))
352 }
353
354 pub fn start_canister(&self, canister_id: &Principal) -> impl 'agent + AsyncCall<Value = ()> {
356 #[derive(CandidType)]
357 struct Argument {
358 canister_id: Principal,
359 }
360
361 self.update(MgmtMethod::StartCanister.as_ref())
362 .with_arg(Argument {
363 canister_id: *canister_id,
364 })
365 .with_effective_canister_id(canister_id.to_owned())
366 .build()
367 }
368
369 pub fn stop_canister(&self, canister_id: &Principal) -> impl 'agent + AsyncCall<Value = ()> {
371 #[derive(CandidType)]
372 struct Argument {
373 canister_id: Principal,
374 }
375
376 self.update(MgmtMethod::StopCanister.as_ref())
377 .with_arg(Argument {
378 canister_id: *canister_id,
379 })
380 .with_effective_canister_id(canister_id.to_owned())
381 .build()
382 }
383
384 pub fn uninstall_code(&self, canister_id: &Principal) -> impl 'agent + AsyncCall<Value = ()> {
392 #[derive(CandidType)]
393 struct Argument {
394 canister_id: Principal,
395 }
396
397 self.update(MgmtMethod::UninstallCode.as_ref())
398 .with_arg(Argument {
399 canister_id: *canister_id,
400 })
401 .with_effective_canister_id(canister_id.to_owned())
402 .build()
403 }
404
405 pub fn install_code<'canister>(
407 &'canister self,
408 canister_id: &Principal,
409 wasm: &'canister [u8],
410 ) -> InstallCodeBuilder<'agent, 'canister> {
411 InstallCodeBuilder::builder(self, canister_id, wasm)
412 }
413
414 pub fn update_settings<'canister>(
416 &'canister self,
417 canister_id: &Principal,
418 ) -> UpdateCanisterBuilder<'agent, 'canister> {
419 UpdateCanisterBuilder::builder(self, canister_id)
420 }
421
422 pub fn upload_chunk(
424 &self,
425 canister_id: &Principal,
426 chunk: &[u8],
427 ) -> impl 'agent + AsyncCall<Value = (UploadChunkResult,)> {
428 #[derive(CandidType, Deserialize)]
429 struct Argument<'a> {
430 canister_id: Principal,
431 #[serde(with = "serde_bytes")]
432 chunk: &'a [u8],
433 }
434
435 self.update(MgmtMethod::UploadChunk.as_ref())
436 .with_arg(Argument {
437 canister_id: *canister_id,
438 chunk,
439 })
440 .with_effective_canister_id(*canister_id)
441 .build()
442 }
443
444 pub fn clear_chunk_store(
446 &self,
447 canister_id: &Principal,
448 ) -> impl 'agent + AsyncCall<Value = ()> {
449 #[derive(CandidType)]
450 struct Argument<'a> {
451 canister_id: &'a Principal,
452 }
453 self.update(MgmtMethod::ClearChunkStore.as_ref())
454 .with_arg(Argument { canister_id })
455 .with_effective_canister_id(*canister_id)
456 .build()
457 }
458
459 pub fn stored_chunks(
461 &self,
462 canister_id: &Principal,
463 ) -> impl 'agent + AsyncCall<Value = (StoreChunksResult,)> {
464 #[derive(CandidType)]
465 struct Argument<'a> {
466 canister_id: &'a Principal,
467 }
468 self.update(MgmtMethod::StoredChunks.as_ref())
469 .with_arg(Argument { canister_id })
470 .with_effective_canister_id(*canister_id)
471 .build()
472 }
473
474 pub fn install_chunked_code<'canister>(
476 &'canister self,
477 canister_id: &Principal,
478 wasm_module_hash: &[u8],
479 ) -> InstallChunkedCodeBuilder<'agent, 'canister> {
480 InstallChunkedCodeBuilder::builder(self, *canister_id, wasm_module_hash)
481 }
482
483 pub fn install<'canister: 'builder, 'builder>(
489 &'canister self,
490 canister_id: &Principal,
491 wasm: &'builder [u8],
492 ) -> InstallBuilder<'agent, 'canister, 'builder> {
493 InstallBuilder::builder(self, canister_id, wasm)
494 }
495
496 pub fn fetch_canister_logs(
498 &self,
499 canister_id: &Principal,
500 ) -> impl 'agent + SyncCall<Value = (FetchCanisterLogsResponse,)> {
501 #[derive(CandidType)]
502 struct In {
503 canister_id: Principal,
504 }
505
506 self.query(MgmtMethod::FetchCanisterLogs.as_ref())
508 .with_arg(In {
509 canister_id: *canister_id,
510 })
511 .with_effective_canister_id(*canister_id)
512 .build()
513 }
514
515 pub fn take_canister_snapshot(
519 &self,
520 canister_id: &Principal,
521 replace_snapshot: Option<&[u8]>,
522 ) -> impl 'agent + AsyncCall<Value = (Snapshot,)> {
523 #[derive(CandidType)]
524 struct In<'a> {
525 canister_id: Principal,
526 replace_snapshot: Option<&'a [u8]>,
527 }
528 self.update(MgmtMethod::TakeCanisterSnapshot.as_ref())
529 .with_arg(In {
530 canister_id: *canister_id,
531 replace_snapshot,
532 })
533 .with_effective_canister_id(*canister_id)
534 .build()
535 }
536
537 pub fn load_canister_snapshot(
541 &self,
542 canister_id: &Principal,
543 snapshot_id: &[u8],
544 ) -> impl 'agent + AsyncCall<Value = ()> {
545 #[derive(CandidType)]
546 struct In<'a> {
547 canister_id: Principal,
548 snapshot_id: &'a [u8],
549 sender_canister_version: Option<u64>,
550 }
551 self.update(MgmtMethod::LoadCanisterSnapshot.as_ref())
552 .with_arg(In {
553 canister_id: *canister_id,
554 snapshot_id,
555 sender_canister_version: None,
556 })
557 .with_effective_canister_id(*canister_id)
558 .build()
559 }
560
561 pub fn list_canister_snapshots(
563 &self,
564 canister_id: &Principal,
565 ) -> impl 'agent + AsyncCall<Value = (Vec<Snapshot>,)> {
566 #[derive(CandidType)]
567 struct In {
568 canister_id: Principal,
569 }
570 self.update(MgmtMethod::ListCanisterSnapshots.as_ref())
571 .with_arg(In {
572 canister_id: *canister_id,
573 })
574 .with_effective_canister_id(*canister_id)
575 .build()
576 }
577
578 pub fn delete_canister_snapshot(
580 &self,
581 canister_id: &Principal,
582 snapshot_id: &[u8],
583 ) -> impl 'agent + AsyncCall<Value = ()> {
584 #[derive(CandidType)]
585 struct In<'a> {
586 canister_id: Principal,
587 snapshot_id: &'a [u8],
588 }
589 self.update(MgmtMethod::DeleteCanisterSnapshot.as_ref())
590 .with_arg(In {
591 canister_id: *canister_id,
592 snapshot_id,
593 })
594 .build()
595 }
596}