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}