ic_cdk/management_canister.rs
1//! Functions and types for interacting with the [IC management canister](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-management-canister).
2//!
3//! # Type Definitions
4//!
5//! This module defines the types of arguments and results for the management canister entry points.
6//! Most of these types are re-exported from the `ic-management-canister-types` crate.
7//!
8//! The only exception is that for the argument types that has a `sender_canister_version` field, this module provides reduced versions instead.
9//! The reduced versions don't need the `sender_canister_version` field as it is set automatically in the corresponding functions.
10//!
11//! # Call
12//!
13//! The majority of the functions in this module are for making calls to the management canister.
14//!
15//! ## Bounded-wait vs. Unbounded-wait
16//!
17//! Interacting with the IC management canister involves making inter-canister calls,
18//! which can be either [bounded-wait](crate::call::Call::bounded_wait) or [unbounded-wait](crate::call::Call::unbounded_wait).
19//! This module selects the appropriate type of wait call for each method based on the characteristics of the entry point.
20//!
21//! The strategy for choosing the type of wait call is as follows:
22//! - Unbounded-wait call by default because the management canister is universally trusted.
23//! - Bounded-wait call (with the default 300s timeout) for methods that only read state.
24//!
25//! Please check the documentation of each function for the type of wait call it uses.
26//!
27//! If the default behavior is not suitable for a particular use case, the [`Call`] struct can be used directly to make the call.
28//!
29//! For example, [`sign_with_ecdsa`] makes an unbounded-wait call. If a bounded-wait call is preferred, the call can be made as follows:
30//! ```rust, no_run
31//! # use ic_cdk::management_canister::{cost_sign_with_ecdsa, SignCallError, SignWithEcdsaArgs, SignWithEcdsaResult};
32//! # use ic_cdk::call::Call;
33//! # use candid::Principal;
34//! # async fn example() -> Result<SignWithEcdsaResult, SignCallError> {
35//! let callee = Principal::management_canister();
36//! let arg = SignWithEcdsaArgs::default();
37//! let cycles = cost_sign_with_ecdsa(&arg)?;
38//! let res: SignWithEcdsaResult = Call::bounded_wait(callee, "sign_with_ecdsa")
39//! .with_arg(&arg)
40//! .with_cycles(cycles)
41//! .await?
42//! .candid()?;
43//! # Ok(res)
44//! # }
45//! ```
46//!
47//! ## Cycle Cost
48//!
49//! Some management canister entry points require cycles to be attached to the call.
50//! The functions for calling management canister automatically calculate the required cycles and attach them to the call.
51//!
52//! For completeness, this module also provides functions to calculate the cycle cost:
53//! - [`cost_http_request`]
54//! - [`cost_sign_with_ecdsa`]
55//! - [`cost_sign_with_schnorr`]
56//! - [`cost_vetkd_derive_key`]
57
58use crate::api::{
59 SignCostError, canister_version, cost_create_canister,
60 cost_http_request as ic0_cost_http_request, cost_sign_with_ecdsa as ic0_cost_sign_with_ecdsa,
61 cost_sign_with_schnorr as ic0_cost_sign_with_schnorr,
62 cost_vetkd_derive_key as ic0_cost_vetkd_derive_key,
63};
64use crate::call::{Call, CallFailed, CallResult, CandidDecodeFailed};
65use candid::{CandidType, Nat, Principal};
66use serde::{Deserialize, Serialize};
67
68// Re-export types from the `ic-management-canister-types` crate.
69pub use ic_management_canister_types::{
70 Bip341, CanisterId, CanisterInfoArgs, CanisterInfoResult, CanisterInstallMode,
71 CanisterSettings, CanisterStatusArgs, CanisterStatusResult, CanisterStatusType, Change,
72 ChangeDetails, ChangeOrigin, ChunkHash, ClearChunkStoreArgs, CodeDeploymentMode,
73 CodeDeploymentRecord, ControllersChangeRecord, CreateCanisterResult, CreationRecord,
74 DefiniteCanisterSettings, DeleteCanisterArgs, DeleteCanisterSnapshotArgs, DepositCyclesArgs,
75 EcdsaCurve, EcdsaKeyId, EcdsaPublicKeyArgs, EcdsaPublicKeyResult, FromCanisterRecord,
76 FromUserRecord, HttpHeader, HttpMethod, HttpRequestArgs, HttpRequestResult,
77 ListCanisterSnapshotsArgs, ListCanisterSnapshotsResult, LoadSnapshotRecord, LogVisibility,
78 NodeMetrics, NodeMetricsHistoryArgs, NodeMetricsHistoryRecord, NodeMetricsHistoryResult,
79 ProvisionalCreateCanisterWithCyclesResult, ProvisionalTopUpCanisterArgs, QueryStats,
80 RawRandResult, SchnorrAlgorithm, SchnorrAux, SchnorrKeyId, SchnorrPublicKeyArgs,
81 SchnorrPublicKeyResult, SignWithEcdsaArgs, SignWithEcdsaResult, SignWithSchnorrArgs,
82 SignWithSchnorrResult, Snapshot, SnapshotId, StartCanisterArgs, StopCanisterArgs,
83 StoredChunksArgs, StoredChunksResult, SubnetInfoArgs, SubnetInfoResult,
84 TakeCanisterSnapshotArgs, TakeCanisterSnapshotResult, TransformArgs, TransformContext,
85 TransformFunc, UpgradeFlags, UploadChunkArgs, UploadChunkResult, VetKDCurve,
86 VetKDDeriveKeyArgs, VetKDDeriveKeyResult, VetKDKeyId, VetKDPublicKeyArgs, VetKDPublicKeyResult,
87 WasmMemoryPersistence, WasmModule,
88};
89// Following Args types contain `sender_canister_version` field which is set automatically in the corresponding functions.
90// We provide reduced versions of these types to avoid duplication of the field.
91use ic_management_canister_types::{
92 CreateCanisterArgs as CreateCanisterArgsComplete,
93 InstallChunkedCodeArgs as InstallChunkedCodeArgsComplete,
94 InstallCodeArgs as InstallCodeArgsComplete,
95 LoadCanisterSnapshotArgs as LoadCanisterSnapshotArgsComplete,
96 ProvisionalCreateCanisterWithCyclesArgs as ProvisionalCreateCanisterWithCyclesArgsComplete,
97 UninstallCodeArgs as UninstallCodeArgsComplete,
98 UpdateSettingsArgs as UpdateSettingsArgsComplete,
99};
100
101/// The error type for the [`sign_with_ecdsa`] and [`sign_with_schnorr`] functions.
102#[derive(thiserror::Error, Debug, Clone)]
103pub enum SignCallError {
104 /// The signature cost calculation failed.
105 #[error(transparent)]
106 SignCostError(#[from] SignCostError),
107 /// Failed to make the inter-canister call to the management canister.
108 #[error(transparent)]
109 CallFailed(#[from] CallFailed),
110 /// Failed to decode the response from the management canister.
111 #[error(transparent)]
112 CandidDecodeFailed(#[from] CandidDecodeFailed),
113}
114
115/// Creates a new canister.
116///
117/// **Unbounded-wait call**
118///
119/// See [IC method `create_canister`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-create_canister).
120///
121/// # Note
122///
123/// Canister creation costs cycles. That amount will be deducted from the newly created canister.
124/// This method will only attach the required cycles for the canister creation (detemined by [`cost_create_canister`]).
125/// The new canister will have a 0 cycle balance.
126///
127/// To ensure the new canister has extra cycles after creation, use [`create_canister_with_extra_cycles`] instead.
128///
129/// Cycles can also be deposited to the new canister using [`deposit_cycles`].
130///
131/// Check [Gas and cycles cost](https://internetcomputer.org/docs/current/developer-docs/gas-cost#canister-creation) for more details.
132pub async fn create_canister(arg: &CreateCanisterArgs) -> CallResult<CreateCanisterResult> {
133 let complete_arg = CreateCanisterArgsComplete {
134 settings: arg.settings.clone(),
135 sender_canister_version: Some(canister_version()),
136 };
137 let cycles = cost_create_canister();
138 Ok(
139 Call::unbounded_wait(Principal::management_canister(), "create_canister")
140 .with_arg(&complete_arg)
141 .with_cycles(cycles)
142 .await?
143 .candid()?,
144 )
145}
146
147/// Creates a new canister with extra cycles.
148///
149/// **Unbounded-wait call**
150///
151/// See [IC method `create_canister`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-create_canister).
152///
153/// # Note
154///
155/// Canister creation costs cycles. That amount will be deducted from the newly created canister.
156/// This method will attach the required cycles for the canister creation (detemined by [`cost_create_canister`]) plus the `extra_cycles` to the call.
157/// The new cansiter will have a cycle balance of `extra_cycles`.
158///
159/// To simply create a canister with 0 cycle balance, use [`create_canister`] instead.
160///
161/// Check [Gas and cycles cost](https://internetcomputer.org/docs/current/developer-docs/gas-cost#canister-creation) for more details.
162pub async fn create_canister_with_extra_cycles(
163 arg: &CreateCanisterArgs,
164 extra_cycles: u128,
165) -> CallResult<CreateCanisterResult> {
166 let complete_arg = CreateCanisterArgsComplete {
167 settings: arg.settings.clone(),
168 sender_canister_version: Some(canister_version()),
169 };
170 let cycles = cost_create_canister() + extra_cycles;
171 Ok(
172 Call::unbounded_wait(Principal::management_canister(), "create_canister")
173 .with_arg(&complete_arg)
174 .with_cycles(cycles)
175 .await?
176 .candid()?,
177 )
178}
179
180/// Argument type of [`create_canister`] and [`create_canister_with_extra_cycles`].
181///
182/// # Note
183///
184/// This type is a reduced version of [`ic_management_canister_types::CreateCanisterArgs`].
185///
186/// The `sender_canister_version` field is removed as it is set automatically in [`create_canister`] and [`create_canister_with_extra_cycles`].
187#[derive(
188 CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default,
189)]
190pub struct CreateCanisterArgs {
191 /// See [`CanisterSettings`].
192 pub settings: Option<CanisterSettings>,
193}
194
195/// Updates the settings of a canister.
196///
197/// **Unbounded-wait call**
198///
199/// See [IC method `update_settings`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-update_settings).
200pub async fn update_settings(arg: &UpdateSettingsArgs) -> CallResult<()> {
201 let complete_arg = UpdateSettingsArgsComplete {
202 canister_id: arg.canister_id,
203 settings: arg.settings.clone(),
204 sender_canister_version: Some(canister_version()),
205 };
206 Ok(
207 Call::unbounded_wait(Principal::management_canister(), "update_settings")
208 .with_arg(&complete_arg)
209 .await?
210 .candid()?,
211 )
212}
213
214/// Argument type of [`update_settings`]
215///
216/// # Note
217///
218/// This type is a reduced version of [`ic_management_canister_types::UpdateSettingsArgs`].
219///
220/// The `sender_canister_version` field is removed as it is set automatically in [`update_settings`].
221#[derive(
222 CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone,
223)]
224pub struct UpdateSettingsArgs {
225 /// Canister ID.
226 pub canister_id: CanisterId,
227 /// See [`CanisterSettings`].
228 pub settings: CanisterSettings,
229}
230
231/// Uploads a chunk to the chunk store of a canister.
232///
233/// **Unbounded-wait call**
234///
235/// See [IC method `upload_chunk`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-upload_chunk).
236pub async fn upload_chunk(arg: &UploadChunkArgs) -> CallResult<UploadChunkResult> {
237 Ok(
238 Call::unbounded_wait(Principal::management_canister(), "upload_chunk")
239 .with_arg(arg)
240 .await?
241 .candid()?,
242 )
243}
244
245/// Clears the chunk store of a canister.
246///
247/// **Unbounded-wait call**
248///
249/// See [IC method `clear_chunk_store`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-clear_chunk_store).
250pub async fn clear_chunk_store(arg: &ClearChunkStoreArgs) -> CallResult<()> {
251 Ok(
252 Call::unbounded_wait(Principal::management_canister(), "clear_chunk_store")
253 .with_arg(arg)
254 .await?
255 .candid()?,
256 )
257}
258
259/// Gets the hashes of all chunks stored in the chunk store of a canister.
260///
261/// **Bounded-wait call**
262///
263/// See [IC method `stored_chunks`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-stored_chunks).
264pub async fn stored_chunks(arg: &StoredChunksArgs) -> CallResult<StoredChunksResult> {
265 Ok(
266 Call::bounded_wait(Principal::management_canister(), "stored_chunks")
267 .with_arg(arg)
268 .await?
269 .candid()?,
270 )
271}
272
273/// Installs code into a canister.
274///
275/// **Unbounded-wait call**
276///
277/// See [IC method `install_code`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-install_code).
278pub async fn install_code(arg: &InstallCodeArgs) -> CallResult<()> {
279 let complete_arg = InstallCodeArgsComplete {
280 mode: arg.mode,
281 canister_id: arg.canister_id,
282 wasm_module: arg.wasm_module.clone(),
283 arg: arg.arg.clone(),
284 sender_canister_version: Some(canister_version()),
285 };
286 Ok(
287 Call::unbounded_wait(Principal::management_canister(), "install_code")
288 .with_arg(&complete_arg)
289 .await?
290 .candid()?,
291 )
292}
293
294/// Argument type of [`install_code`].
295///
296/// # Note
297///
298/// This type is a reduced version of [`ic_management_canister_types::InstallCodeArgs`].
299///
300/// The `sender_canister_version` field is removed as it is set automatically in [`install_code`].
301#[derive(
302 CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone,
303)]
304pub struct InstallCodeArgs {
305 /// See [`CanisterInstallMode`].
306 pub mode: CanisterInstallMode,
307 /// Canister ID.
308 pub canister_id: CanisterId,
309 /// Code to be installed.
310 pub wasm_module: WasmModule,
311 /// The argument to be passed to `canister_init` or `canister_post_upgrade`.
312 #[serde(with = "serde_bytes")]
313 pub arg: Vec<u8>,
314}
315
316/// Installs code into a canister where the code has previously been uploaded in chunks.
317///
318/// **Unbounded-wait call**
319///
320/// See [IC method `install_chunked_code`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-install_chunked_code).
321pub async fn install_chunked_code(arg: &InstallChunkedCodeArgs) -> CallResult<()> {
322 let complete_arg = InstallChunkedCodeArgsComplete {
323 mode: arg.mode,
324 target_canister: arg.target_canister,
325 store_canister: arg.store_canister,
326 chunk_hashes_list: arg.chunk_hashes_list.clone(),
327 wasm_module_hash: arg.wasm_module_hash.clone(),
328 arg: arg.arg.clone(),
329 sender_canister_version: Some(canister_version()),
330 };
331 Ok(
332 Call::unbounded_wait(Principal::management_canister(), "install_chunked_code")
333 .with_arg(&complete_arg)
334 .await?
335 .candid()?,
336 )
337}
338
339/// Argument type of [`install_chunked_code`].
340///
341/// # Note
342///
343/// This type is a reduced version of [`ic_management_canister_types::InstallChunkedCodeArgs`].
344///
345/// The `sender_canister_version` field is removed as it is set automatically in [`install_chunked_code`].
346#[derive(
347 CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone,
348)]
349pub struct InstallChunkedCodeArgs {
350 /// See [`CanisterInstallMode`].
351 pub mode: CanisterInstallMode,
352 /// Principal of the canister being installed.
353 pub target_canister: CanisterId,
354 /// The canister in whose chunk storage the chunks are stored (defaults to `target_canister` if not specified).
355 pub store_canister: Option<CanisterId>,
356 /// The list of chunks that make up the canister wasm.
357 pub chunk_hashes_list: Vec<ChunkHash>,
358 /// The sha256 hash of the wasm.
359 #[serde(with = "serde_bytes")]
360 pub wasm_module_hash: Vec<u8>,
361 /// The argument to be passed to `canister_init` or `canister_post_upgrade`.
362 #[serde(with = "serde_bytes")]
363 pub arg: Vec<u8>,
364}
365
366/// Removes a canister's code and state, making the canister empty again.
367///
368/// **Unbounded-wait call**
369///
370/// See [IC method `uninstall_code`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-uninstall_code).
371pub async fn uninstall_code(arg: &UninstallCodeArgs) -> CallResult<()> {
372 let complete_arg = UninstallCodeArgsComplete {
373 canister_id: arg.canister_id,
374 sender_canister_version: Some(canister_version()),
375 };
376 Ok(
377 Call::unbounded_wait(Principal::management_canister(), "uninstall_code")
378 .with_arg(&complete_arg)
379 .await?
380 .candid()?,
381 )
382}
383
384/// Argument type of [`uninstall_code`].
385///
386/// # Note
387///
388/// This type is a reduced version of [`ic_management_canister_types::UninstallCodeArgs`].
389///
390/// The `sender_canister_version` field is removed as it is set automatically in [`uninstall_code`].
391#[derive(
392 CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone,
393)]
394pub struct UninstallCodeArgs {
395 /// Canister ID.
396 pub canister_id: CanisterId,
397}
398
399/// Starts a canister if the canister status was `stopped` or `stopping`.
400///
401/// **Unbounded-wait call**
402///
403/// See [IC method `start_canister`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-start_canister).
404pub async fn start_canister(arg: &StartCanisterArgs) -> CallResult<()> {
405 Ok(
406 Call::unbounded_wait(Principal::management_canister(), "start_canister")
407 .with_arg(arg)
408 .await?
409 .candid()?,
410 )
411}
412
413/// Stops a canister.
414///
415/// **Unbounded-wait call**
416///
417/// See [IC method `stop_canister`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-stop_canister).
418pub async fn stop_canister(arg: &StopCanisterArgs) -> CallResult<()> {
419 Ok(
420 Call::unbounded_wait(Principal::management_canister(), "stop_canister")
421 .with_arg(arg)
422 .await?
423 .candid()?,
424 )
425}
426
427/// Gets status information about the canister.
428///
429/// **Bounded-wait call**
430///
431/// See [IC method `canister_status`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-canister_status).
432pub async fn canister_status(arg: &CanisterStatusArgs) -> CallResult<CanisterStatusResult> {
433 Ok(
434 Call::bounded_wait(Principal::management_canister(), "canister_status")
435 .with_arg(arg)
436 .await?
437 .candid()?,
438 )
439}
440
441/// Gets public information about the canister.
442///
443/// **Bounded-wait call**
444///
445/// See [IC method `canister_info`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-canister_info).
446pub async fn canister_info(arg: &CanisterInfoArgs) -> CallResult<CanisterInfoResult> {
447 Ok(
448 Call::bounded_wait(Principal::management_canister(), "canister_info")
449 .with_arg(arg)
450 .await?
451 .candid()?,
452 )
453}
454
455/// Deletes a canister.
456///
457/// **Unbounded-wait call**
458///
459/// See [IC method `delete_canister`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-delete_canister).
460pub async fn delete_canister(arg: &DeleteCanisterArgs) -> CallResult<()> {
461 Ok(
462 Call::unbounded_wait(Principal::management_canister(), "delete_canister")
463 .with_arg(arg)
464 .await?
465 .candid()?,
466 )
467}
468
469/// Deposits cycles to a canister.
470///
471/// **Unbounded-wait call**
472///
473/// See [IC method `deposit_cycles`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-deposit_cycles).
474pub async fn deposit_cycles(arg: &DepositCyclesArgs, cycles: u128) -> CallResult<()> {
475 Ok(
476 Call::unbounded_wait(Principal::management_canister(), "deposit_cycles")
477 .with_arg(arg)
478 .with_cycles(cycles)
479 .await?
480 .candid()?,
481 )
482}
483
484/// Gets 32 pseudo-random bytes.
485///
486/// **Bounded-wait call**
487///
488/// See [IC method `raw_rand`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-raw_rand).
489pub async fn raw_rand() -> CallResult<RawRandResult> {
490 Ok(
491 Call::bounded_wait(Principal::management_canister(), "raw_rand")
492 .await?
493 .candid()?,
494 )
495}
496
497/// Calculates the cost of making an HTTP outcall with the given [`HttpRequestArgs`].
498///
499/// [`http_request`] and [`http_request_with_closure`] invoke this method internally and attach the required cycles to the call.
500///
501/// # Note
502///
503/// Alternatively, [`api::cost_http_request`][ic0_cost_http_request] requires manually calculating the request size and the maximum response size.
504/// This method handles the calculation internally.
505pub fn cost_http_request(arg: &HttpRequestArgs) -> u128 {
506 let request_size = (arg.url.len()
507 + arg
508 .headers
509 .iter()
510 .map(|h| h.name.len() + h.value.len())
511 .sum::<usize>()
512 + arg.body.as_ref().map_or(0, |b| b.len())
513 + arg
514 .transform
515 .as_ref()
516 .map_or(0, |t| t.context.len() + t.function.0.method.len()))
517 as u64;
518 // As stated here: https://internetcomputer.org/docs/references/ic-interface-spec#ic-http_request:
519 // "The upper limit on the maximal size for the response is 2MB (2,000,000B) and this value also applies if no maximal size value is specified."
520 let max_res_bytes = arg.max_response_bytes.unwrap_or(2_000_000);
521 ic0_cost_http_request(request_size, max_res_bytes)
522}
523
524/// Makes an HTTP outcall with a user-specified amount of cycles.
525///
526/// **Unbounded-wait call**
527///
528/// See [IC method `http_request`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-http_request).
529///
530/// # Note
531///
532/// HTTP outcall costs cycles which varies with the request size and the maximum response size.
533/// This method attaches the required cycles (detemined by [`cost_http_request`]) to the call.
534///
535/// Check [HTTPS outcalls cycles cost](https://internetcomputer.org/docs/current/developer-docs/gas-cost#https-outcalls) for more details.
536pub async fn http_request(arg: &HttpRequestArgs) -> CallResult<HttpRequestResult> {
537 let cycles = cost_http_request(arg);
538 Ok(
539 Call::unbounded_wait(Principal::management_canister(), "http_request")
540 .with_arg(arg)
541 .with_cycles(cycles)
542 .await?
543 .candid()?,
544 )
545}
546
547/// Constructs a [`TransformContext`] from a query method name and context.
548pub fn transform_context_from_query(
549 candid_function_name: String,
550 context: Vec<u8>,
551) -> TransformContext {
552 TransformContext {
553 context,
554 function: TransformFunc(candid::Func {
555 method: candid_function_name,
556 principal: crate::api::canister_self(),
557 }),
558 }
559}
560
561#[cfg(feature = "transform-closure")]
562mod transform_closure {
563 use super::{
564 CallResult, HttpRequestArgs, HttpRequestResult, Principal, TransformArgs, http_request,
565 transform_context_from_query,
566 };
567 use candid::{decode_one, encode_one};
568 use ic_cdk_executor::in_query_executor_context;
569 use slotmap::{DefaultKey, Key, KeyData, SlotMap};
570 use std::cell::RefCell;
571
572 thread_local! {
573 #[allow(clippy::type_complexity)]
574 static TRANSFORMS: RefCell<SlotMap<DefaultKey, Box<dyn FnOnce(HttpRequestResult) -> HttpRequestResult>>> = RefCell::default();
575 }
576
577 #[unsafe(export_name = "canister_query <ic-cdk internal> http_transform")]
578 extern "C" fn http_transform() {
579 in_query_executor_context(|| {
580 use crate::api::{msg_arg_data, msg_caller, msg_reply};
581 if msg_caller() != Principal::management_canister() {
582 crate::trap(
583 "This function is internal to ic-cdk and should not be called externally.",
584 );
585 }
586 let arg_bytes = msg_arg_data();
587 let transform_args: TransformArgs = decode_one(&arg_bytes).unwrap();
588 let int = u64::from_be_bytes(transform_args.context[..].try_into().unwrap());
589 let key = DefaultKey::from(KeyData::from_ffi(int));
590 let func = TRANSFORMS.with(|transforms| transforms.borrow_mut().remove(key));
591 let Some(func) = func else {
592 crate::trap(format!("Missing transform function for request {int}"));
593 };
594 let transformed = func(transform_args.response);
595 let encoded = encode_one(transformed).unwrap();
596 msg_reply(encoded);
597 });
598 }
599
600 /// Makes an HTTP outcall and transforms the response using a closure.
601 ///
602 /// **Unbounded-wait call**
603 ///
604 /// See [IC method `http_request`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-http_request).
605 ///
606 /// # Panics
607 ///
608 /// This method will panic if the `transform` field in `arg` is not `None`,
609 /// as it would conflict with the transform function provided by the closure.
610 ///
611 /// # Note
612 ///
613 /// This method provides a straightforward way to transform the HTTP outcall result.
614 /// If you need to specify a custom transform [`context`](`ic_management_canister_types::TransformContext::context`),
615 /// please use [`http_request`] instead.
616 ///
617 /// HTTP outcall costs cycles which varies with the request size and the maximum response size.
618 /// This method attaches the required cycles (detemined by [`cost_http_request`](crate::api::cost_http_request)) to the call.
619 ///
620 /// Check [Gas and cycles cost](https://internetcomputer.org/docs/current/developer-docs/gas-cost) for more details.
621 #[cfg_attr(docsrs, doc(cfg(feature = "transform-closure")))]
622 pub async fn http_request_with_closure(
623 arg: &HttpRequestArgs,
624 transform_func: impl FnOnce(HttpRequestResult) -> HttpRequestResult + 'static,
625 ) -> CallResult<HttpRequestResult> {
626 assert!(
627 arg.transform.is_none(),
628 "The `transform` field in `HttpRequestArgs` must be `None` when using a closure"
629 );
630 let transform_func = Box::new(transform_func) as _;
631 let key = TRANSFORMS.with(|transforms| transforms.borrow_mut().insert(transform_func));
632 struct DropGuard(DefaultKey);
633 impl Drop for DropGuard {
634 fn drop(&mut self) {
635 TRANSFORMS.with(|transforms| transforms.borrow_mut().remove(self.0));
636 }
637 }
638 let key = DropGuard(key);
639 let context = key.0.data().as_ffi().to_be_bytes().to_vec();
640 let arg = HttpRequestArgs {
641 transform: Some(transform_context_from_query(
642 "<ic-cdk internal> http_transform".to_string(),
643 context,
644 )),
645 ..arg.clone()
646 };
647 http_request(&arg).await
648 }
649}
650
651#[cfg(feature = "transform-closure")]
652pub use transform_closure::http_request_with_closure;
653
654/// Gets a SEC1 encoded ECDSA public key for the given canister using the given derivation path.
655///
656/// **Bounded-wait call**
657///
658/// See [IC method `ecdsa_public_key`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-ecdsa_public_key).
659pub async fn ecdsa_public_key(arg: &EcdsaPublicKeyArgs) -> CallResult<EcdsaPublicKeyResult> {
660 Ok(
661 Call::bounded_wait(Principal::management_canister(), "ecdsa_public_key")
662 .with_arg(arg)
663 .await?
664 .candid()?,
665 )
666}
667
668/// Calculates the cost of ECDSA signanature with the given [`SignWithEcdsaArgs`].
669///
670/// [`sign_with_ecdsa`] invokes this method internally and attaches the required cycles to the call.
671///
672/// # Note
673///
674/// Alternatively, [`api::cost_sign_with_ecdsa`][ic0_cost_sign_with_ecdsa] takes the numeric representation of the curve.
675pub fn cost_sign_with_ecdsa(arg: &SignWithEcdsaArgs) -> Result<u128, SignCostError> {
676 ic0_cost_sign_with_ecdsa(&arg.key_id.name, arg.key_id.curve.into())
677}
678
679/// Gets a new ECDSA signature of the given `message_hash` with a user-specified amount of cycles.
680///
681/// **Unbounded-wait call**
682///
683/// The signature can be separately verified against a derived ECDSA public key.
684///
685/// See [IC method `sign_with_ecdsa`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-sign_with_ecdsa).
686///
687/// # Errors
688///
689/// This method returns an error of type [`SignCallError`].
690///
691/// The signature cost calculation may fail before the inter-canister call is made, resulting in a [`SignCallError::SignCostError`].
692///
693/// Since the call argument is constructed as [`SignWithEcdsaArgs`], the `ecdsa_curve` field is guaranteed to be valid.
694/// Therefore, [`SignCostError::InvalidCurveOrAlgorithm`] should not occur. If it does, it is likely an issue with the IC. Please report it.
695///
696/// # Note
697///
698/// Signature costs cycles which varies for different curves and key names.
699/// This method attaches the required cycles (detemined by [`cost_sign_with_ecdsa`]) to the call.
700///
701/// Check [Threshold signatures](https://internetcomputer.org/docs/current/references/t-sigs-how-it-works/#api-fees) for more details.
702pub async fn sign_with_ecdsa(
703 arg: &SignWithEcdsaArgs,
704) -> Result<SignWithEcdsaResult, SignCallError> {
705 let cycles = cost_sign_with_ecdsa(arg)?;
706 Ok(
707 Call::unbounded_wait(Principal::management_canister(), "sign_with_ecdsa")
708 .with_arg(arg)
709 .with_cycles(cycles)
710 .await?
711 .candid()?,
712 )
713}
714
715/// Gets a SEC1 encoded Schnorr public key for the given canister using the given derivation path.
716///
717/// **Bounded-wait call**
718///
719/// See [IC method `schnorr_public_key`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-schnorr_public_key).
720pub async fn schnorr_public_key(arg: &SchnorrPublicKeyArgs) -> CallResult<SchnorrPublicKeyResult> {
721 Ok(
722 Call::bounded_wait(Principal::management_canister(), "schnorr_public_key")
723 .with_arg(arg)
724 .await?
725 .candid()?,
726 )
727}
728
729/// Calculates the cost of Schnorr signanature with the given [`SignWithSchnorrArgs`].
730///
731/// [`sign_with_schnorr`] invokes this method internally and attaches the required cycles to the call.
732///
733/// # Note
734///
735/// Alternatively, [`api::cost_sign_with_schnorr`][ic0_cost_sign_with_schnorr] takes the numeric representation of the algorithm.
736pub fn cost_sign_with_schnorr(arg: &SignWithSchnorrArgs) -> Result<u128, SignCostError> {
737 ic0_cost_sign_with_schnorr(&arg.key_id.name, arg.key_id.algorithm.into())
738}
739
740/// Gets a new Schnorr signature of the given message with a user-specified amount of cycles.
741///
742/// **Unbounded-wait call**
743///
744/// The signature can be separately verified against a derived Schnorr public key.
745///
746/// See [IC method `sign_with_schnorr`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-sign_with_schnorr).
747///
748/// # Errors
749///
750/// This method returns an error of type [`SignCallError`].
751///
752/// The signature cost calculation may fail before the inter-canister call is made, resulting in a [`SignCallError::SignCostError`].
753///
754/// Since the call argument is constructed as [`SignWithSchnorrArgs`], the `algorithm` field is guaranteed to be valid.
755/// Therefore, [`SignCostError::InvalidCurveOrAlgorithm`] should not occur. If it does, it is likely an issue with the IC. Please report it.
756///
757/// # Note
758///
759/// Signature costs cycles which varies for different algorithms and key names.
760/// This method attaches the required cycles (detemined by [`cost_sign_with_schnorr`]) to the call.
761///
762/// Check [Threshold signatures](https://internetcomputer.org/docs/current/references/t-sigs-how-it-works/#api-fees) for more details.
763pub async fn sign_with_schnorr(
764 arg: &SignWithSchnorrArgs,
765) -> Result<SignWithSchnorrResult, SignCallError> {
766 let cycles = cost_sign_with_schnorr(arg)?;
767 Ok(
768 Call::unbounded_wait(Principal::management_canister(), "sign_with_schnorr")
769 .with_arg(arg)
770 .with_cycles(cycles)
771 .await?
772 .candid()?,
773 )
774}
775
776/// Gets a VetKD public key.
777///
778/// **Bounded-wait call**
779///
780/// As of 2025-05-01, the vetKD feature is not yet available on the IC mainnet.
781/// The lastest PocketIC with the `with_nonmainnet_features(true)` flag can be used to test it.
782///
783/// See [IC method `vetkd_public_key`](https://github.com/dfinity/portal/pull/3763).
784///
785/// Later, the description will be available in [the interface spec](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-vetkd_public_key).
786pub async fn vetkd_public_key(arg: &VetKDPublicKeyArgs) -> CallResult<VetKDPublicKeyResult> {
787 Ok(
788 Call::bounded_wait(Principal::management_canister(), "vetkd_public_key")
789 .with_arg(arg)
790 .await?
791 .candid()?,
792 )
793}
794
795/// Calculates the cost of VetKD key derivation with the given [`VetKDDeriveKeyArgs`].
796///
797/// [`vetkd_derive_key`] invokes this method internally and attaches the required cycles to the call.
798///
799/// # Note
800///
801/// Alternatively, [`api::cost_vetkd_derive_key`][ic0_cost_vetkd_derive_key] takes the numeric representation of the algorithm.
802pub fn cost_vetkd_derive_key(arg: &VetKDDeriveKeyArgs) -> Result<u128, SignCostError> {
803 ic0_cost_vetkd_derive_key(&arg.key_id.name, arg.key_id.curve.into())
804}
805
806/// Derives a key from the given input.
807///
808/// **Unbounded-wait call**
809///
810/// The returned encrypted key can be separately decrypted using the private secret key corresponding to the transport public key provided in the request, and the derivation correctness can be verified against the input and context provided in the request. See the [`ic_vetkeys` frontend library](https://github.com/dfinity/vetkd-devkit/tree/main/frontend/ic_vetkeys) for more details.
811///
812/// As of 2025-05-01, the vetKD feature is not yet available on the IC mainnet.
813/// The lastest PocketIC with the `with_nonmainnet_features(true)` flag can be used to test it.
814///
815/// See [IC method `vetkd_derive_key`](https://github.com/dfinity/portal/pull/3763) for the API specification.
816///
817/// Later, the description will be available in [the interface spec](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-vetkd_derive_key).
818///
819/// # Errors
820///
821/// This method returns an error of type [`SignCallError`].
822///
823/// The signature cost calculation may fail before the inter-canister call is made, resulting in a [`SignCallError::SignCostError`].
824///
825/// Since the call argument is constructed as [`VetKDDeriveKeyArgs`], the `curve` field is guaranteed to be valid.
826/// Therefore, [`SignCostError::InvalidCurveOrAlgorithm`] should not occur. If it does, it is likely an issue with the IC. Please report it.
827///
828/// # Note
829///
830/// VetKD key derivation costs cycles which varies for different algorithms and key names.
831/// This method attaches the required cycles (detemined by [`cost_vetkd_derive_key`]) to the call.
832///
833/// Check [Threshold signatures](https://internetcomputer.org/docs/current/references/t-sigs-how-it-works/#api-fees) for more details.
834pub async fn vetkd_derive_key(
835 arg: &VetKDDeriveKeyArgs,
836) -> Result<VetKDDeriveKeyResult, SignCallError> {
837 let cycles = cost_vetkd_derive_key(arg)?;
838 Ok(
839 Call::unbounded_wait(Principal::management_canister(), "vetkd_derive_key")
840 .with_arg(arg)
841 .with_cycles(cycles)
842 .await?
843 .candid()?,
844 )
845}
846
847/// Gets a time series of subnet's node metrics.
848///
849/// **Bounded-wait call**
850///
851/// See [IC method `node_metrics_history`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-node_metrics_history).
852pub async fn node_metrics_history(
853 arg: &NodeMetricsHistoryArgs,
854) -> CallResult<NodeMetricsHistoryResult> {
855 Ok(
856 Call::bounded_wait(Principal::management_canister(), "node_metrics_history")
857 .with_arg(arg)
858 .await?
859 .candid()?,
860 )
861}
862
863/// Gets the metadata about a subnet.
864///
865/// **Bounded-wait call**
866///
867/// See [IC method `subnet_info`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-subnet_info).
868pub async fn subnet_info(arg: &SubnetInfoArgs) -> CallResult<SubnetInfoResult> {
869 Ok(
870 Call::bounded_wait(Principal::management_canister(), "subnet_info")
871 .with_arg(arg)
872 .await?
873 .candid()?,
874 )
875}
876
877/// Creates a new canister with specified amount of cycles balance.
878///
879/// **Unbounded-wait call**
880///
881/// # Note
882///
883/// This method is only available in local development instances.
884///
885/// See [IC method `provisional_create_canister_with_cycles`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-provisional_create_canister_with_cycles).
886pub async fn provisional_create_canister_with_cycles(
887 arg: &ProvisionalCreateCanisterWithCyclesArgs,
888) -> CallResult<ProvisionalCreateCanisterWithCyclesResult> {
889 let complete_arg = ProvisionalCreateCanisterWithCyclesArgsComplete {
890 amount: arg.amount.clone(),
891 settings: arg.settings.clone(),
892 specified_id: arg.specified_id,
893 sender_canister_version: Some(canister_version()),
894 };
895 Ok(Call::unbounded_wait(
896 Principal::management_canister(),
897 "provisional_create_canister_with_cycles",
898 )
899 .with_arg(&complete_arg)
900 .await?
901 .candid()?)
902}
903
904/// Argument type of [`provisional_create_canister_with_cycles`].
905///
906/// # Note
907///
908/// This type is a reduced version of [`ic_management_canister_types::ProvisionalCreateCanisterWithCyclesArgs`].
909///
910/// The `sender_canister_version` field is removed as it is set automatically in [`provisional_create_canister_with_cycles`].
911#[derive(
912 CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default,
913)]
914pub struct ProvisionalCreateCanisterWithCyclesArgs {
915 /// The created canister will have this amount of cycles.
916 pub amount: Option<Nat>,
917 /// Canister settings.
918 pub settings: Option<CanisterSettings>,
919 /// If set, the canister will be created under this id.
920 pub specified_id: Option<CanisterId>,
921}
922
923/// Adds cycles to a canister.
924///
925/// **Unbounded-wait call**
926///
927/// # Note
928///
929/// This method is only available in local development instances.
930///
931/// See [IC method `provisional_top_up_canister`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-provisional_top_up_canister).
932pub async fn provisional_top_up_canister(arg: &ProvisionalTopUpCanisterArgs) -> CallResult<()> {
933 Ok(Call::unbounded_wait(
934 Principal::management_canister(),
935 "provisional_top_up_canister",
936 )
937 .with_arg(arg)
938 .await?
939 .candid()?)
940}
941
942/// Takes a snapshot of the specified canister.
943///
944/// **Unbounded-wait call**
945///
946/// A snapshot consists of the wasm memory, stable memory, certified variables, wasm chunk store and wasm binary.
947///
948/// See [IC method `take_canister_snapshot`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-take_canister_snapshot).
949pub async fn take_canister_snapshot(
950 arg: &TakeCanisterSnapshotArgs,
951) -> CallResult<TakeCanisterSnapshotResult> {
952 Ok(
953 Call::unbounded_wait(Principal::management_canister(), "take_canister_snapshot")
954 .with_arg(arg)
955 .await?
956 .candid()?,
957 )
958}
959
960/// Loads a snapshot onto the canister.
961///
962/// **Unbounded-wait call**
963///
964/// It fails if no snapshot with the specified `snapshot_id` can be found.
965///
966/// See [IC method `load_canister_snapshot`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-load_canister_snapshot).
967pub async fn load_canister_snapshot(arg: &LoadCanisterSnapshotArgs) -> CallResult<()> {
968 let complete_arg = LoadCanisterSnapshotArgsComplete {
969 canister_id: arg.canister_id,
970 snapshot_id: arg.snapshot_id.clone(),
971 sender_canister_version: Some(canister_version()),
972 };
973 Ok(
974 Call::unbounded_wait(Principal::management_canister(), "load_canister_snapshot")
975 .with_arg(&complete_arg)
976 .await?
977 .candid()?,
978 )
979}
980
981/// Argument type of [`load_canister_snapshot`].
982///
983/// # Note
984///
985/// This type is a reduced version of [`ic_management_canister_types::LoadCanisterSnapshotArgs`].
986///
987/// The `sender_canister_version` field is removed as it is set automatically in [`load_canister_snapshot`].
988#[derive(
989 CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone,
990)]
991pub struct LoadCanisterSnapshotArgs {
992 /// Canister ID.
993 pub canister_id: CanisterId,
994 /// ID of the snapshot to be loaded.
995 pub snapshot_id: SnapshotId,
996}
997
998/// Lists the snapshots of the canister.
999///
1000/// **Bounded-wait call**
1001///
1002/// See [IC method `list_canister_snapshots`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-list_canister_snapshots).
1003pub async fn list_canister_snapshots(
1004 arg: &ListCanisterSnapshotsArgs,
1005) -> CallResult<ListCanisterSnapshotsResult> {
1006 Ok(
1007 Call::bounded_wait(Principal::management_canister(), "list_canister_snapshots")
1008 .with_arg(arg)
1009 .await?
1010 .candid()?,
1011 )
1012}
1013
1014/// Deletes a specified snapshot that belongs to an existing canister.
1015///
1016/// **Unbounded-wait call**
1017///
1018/// An error will be returned if the snapshot is not found.
1019///
1020/// See [IC method `delete_canister_snapshot`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-delete_canister_snapshot).
1021pub async fn delete_canister_snapshot(arg: &DeleteCanisterSnapshotArgs) -> CallResult<()> {
1022 Ok(
1023 Call::unbounded_wait(Principal::management_canister(), "delete_canister_snapshot")
1024 .with_arg(arg)
1025 .await?
1026 .candid()?,
1027 )
1028}