Skip to main content

ic_cdk_management_canister/
lib.rs

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