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}