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