ic_cdk/api/call.rs
1//! APIs to make and manage calls in the canister.
2use crate::api::trap;
3use candid::utils::{decode_args_with_config_debug, ArgumentDecoder, ArgumentEncoder};
4use candid::{
5 decode_args, encode_args, write_args, CandidType, DecoderConfig, Deserialize, Principal,
6};
7use serde::ser::Error;
8use std::future::Future;
9use std::marker::PhantomData;
10use std::pin::Pin;
11use std::sync::atomic::Ordering;
12use std::sync::{Arc, RwLock, Weak};
13use std::task::{Context, Poll, Waker};
14
15/// Rejection code from calling another canister.
16///
17/// These can be obtained either using `reject_code()` or `reject_result()`.
18#[allow(missing_docs)]
19#[repr(i32)]
20#[derive(CandidType, Deserialize, Clone, Copy, Hash, Debug, PartialEq, Eq, PartialOrd, Ord)]
21pub enum RejectionCode {
22 NoError = 0,
23
24 SysFatal = 1,
25 SysTransient = 2,
26 DestinationInvalid = 3,
27 CanisterReject = 4,
28 CanisterError = 5,
29
30 Unknown,
31}
32
33impl From<i32> for RejectionCode {
34 fn from(code: i32) -> Self {
35 match code {
36 0 => RejectionCode::NoError,
37 1 => RejectionCode::SysFatal,
38 2 => RejectionCode::SysTransient,
39 3 => RejectionCode::DestinationInvalid,
40 4 => RejectionCode::CanisterReject,
41 5 => RejectionCode::CanisterError,
42 _ => RejectionCode::Unknown,
43 }
44 }
45}
46
47impl From<u32> for RejectionCode {
48 fn from(code: u32) -> Self {
49 RejectionCode::from(code as i32)
50 }
51}
52
53/// The result of a Call.
54///
55/// Errors on the IC have two components; a Code and a message associated with it.
56pub type CallResult<R> = Result<R, (RejectionCode, String)>;
57
58// Internal state for the Future when sending a call.
59struct CallFutureState<T: AsRef<[u8]>> {
60 result: Option<CallResult<Vec<u8>>>,
61 waker: Option<Waker>,
62 id: Principal,
63 method: String,
64 arg: T,
65 payment: u128,
66}
67
68struct CallFuture<T: AsRef<[u8]>> {
69 state: Arc<RwLock<CallFutureState<T>>>,
70}
71
72impl<T: AsRef<[u8]>> Future for CallFuture<T> {
73 type Output = CallResult<Vec<u8>>;
74
75 fn poll(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
76 let self_ref = Pin::into_inner(self);
77 let mut state = self_ref.state.write().unwrap();
78
79 if let Some(result) = state.result.take() {
80 Poll::Ready(result)
81 } else {
82 if state.waker.is_none() {
83 let callee = state.id.as_slice();
84 let method = &state.method;
85 let args = state.arg.as_ref();
86 let payment = state.payment;
87 let state_ptr = Weak::into_raw(Arc::downgrade(&self_ref.state));
88 // SAFETY:
89 // `callee`, being &[u8], is a readable sequence of bytes and therefore can be passed to ic0.call_new.
90 // `method`, being &str, is a readable sequence of bytes and therefore can be passed to ic0.call_new.
91 // `callback` is a function with signature (env : i32) -> () and therefore can be called as both reply and reject fn for ic0.call_new.
92 // `state_ptr` is a pointer created via Weak::into_raw, and can therefore be passed as the userdata for `callback`.
93 // `args`, being a &[u8], is a readable sequence of bytes and therefore can be passed to ic0.call_data_append.
94 // `cleanup` is a function with signature (env : i32) -> () and therefore can be called as a cleanup fn for ic0.call_on_cleanup.
95 // `state_ptr` is a pointer created via Weak::into_raw, and can therefore be passed as the userdata for `cleanup`.
96 // ic0.call_perform is always safe to call.
97 // callback and cleanup are safe to parameterize with T because:
98 // - if the future is dropped before the callback is called, there will be no more strong references and the weak reference will fail to upgrade
99 // - if the future is *not* dropped before the callback is called, the compiler will mandate that any data borrowed by T is still alive
100 let err_code = unsafe {
101 ic0::call_new(
102 callee.as_ptr() as i32,
103 callee.len() as i32,
104 method.as_ptr() as i32,
105 method.len() as i32,
106 callback::<T> as usize as i32,
107 state_ptr as i32,
108 callback::<T> as usize as i32,
109 state_ptr as i32,
110 );
111
112 ic0::call_data_append(args.as_ptr() as i32, args.len() as i32);
113 add_payment(payment);
114 ic0::call_on_cleanup(cleanup::<T> as usize as i32, state_ptr as i32);
115 ic0::call_perform()
116 };
117
118 // 0 is a special error code meaning call succeeded.
119 if err_code != 0 {
120 let result = Err((
121 RejectionCode::from(err_code),
122 "Couldn't send message".to_string(),
123 ));
124 state.result = Some(result.clone());
125 return Poll::Ready(result);
126 }
127 }
128 state.waker = Some(context.waker().clone());
129 Poll::Pending
130 }
131 }
132}
133
134/// The callback from IC dereferences the future from a raw pointer, assigns the
135/// result and calls the waker. We cannot use a closure here because we pass raw
136/// pointers to the System and back.
137///
138/// # Safety
139///
140/// This function must only be passed to the IC with a pointer from Weak::into_raw as userdata.
141unsafe extern "C" fn callback<T: AsRef<[u8]>>(state_ptr: *const RwLock<CallFutureState<T>>) {
142 // SAFETY: This function is only ever called by the IC, and we only ever pass a Weak as userdata.
143 let state = unsafe { Weak::from_raw(state_ptr) };
144 if let Some(state) = state.upgrade() {
145 // Make sure to un-borrow_mut the state.
146 {
147 state.write().unwrap().result = Some(match reject_code() {
148 RejectionCode::NoError => Ok(arg_data_raw()),
149 n => Err((n, reject_message())),
150 });
151 }
152 let w = state.write().unwrap().waker.take();
153 if let Some(waker) = w {
154 // This is all to protect this little guy here which will call the poll() which
155 // borrow_mut() the state as well. So we need to be careful to not double-borrow_mut.
156 waker.wake()
157 }
158 }
159}
160
161/// This function is called when [callback] was just called with the same parameter, and trapped.
162/// We can't guarantee internal consistency at this point, but we can at least e.g. drop mutex guards.
163/// Waker is a very opaque API, so the best we can do is set a global flag and proceed normally.
164///
165/// # Safety
166///
167/// This function must only be passed to the IC with a pointer from Weak::into_raw as userdata.
168unsafe extern "C" fn cleanup<T: AsRef<[u8]>>(state_ptr: *const RwLock<CallFutureState<T>>) {
169 // SAFETY: This function is only ever called by the IC, and we only ever pass a Weak as userdata.
170 let state = unsafe { Weak::from_raw(state_ptr) };
171 if let Some(state) = state.upgrade() {
172 // We set the call result, even though it won't be read on the
173 // default executor, because we can't guarantee it was called on
174 // our executor. However, we are not allowed to inspect
175 // reject_code() inside of a cleanup callback, so always set the
176 // result to a reject.
177 //
178 // Borrowing does not trap - the rollback from the
179 // previous trap ensures that the RwLock can be borrowed again.
180 state.write().unwrap().result = Some(Err((RejectionCode::NoError, "cleanup".to_string())));
181 let w = state.write().unwrap().waker.take();
182 if let Some(waker) = w {
183 // Flag that we do not want to actually wake the task - we
184 // want to drop it *without* executing it.
185 ic_cdk_executor::CLEANUP.store(true, Ordering::Relaxed);
186 waker.wake();
187 ic_cdk_executor::CLEANUP.store(false, Ordering::Relaxed);
188 }
189 }
190}
191
192fn add_payment(payment: u128) {
193 if payment == 0 {
194 return;
195 }
196 let high = (payment >> 64) as u64;
197 let low = (payment & u64::MAX as u128) as u64;
198 // SAFETY: ic0.call_cycles_add128 is always safe to call.
199 unsafe {
200 ic0::call_cycles_add128(high as i64, low as i64);
201 }
202}
203
204/// Sends a one-way message with `payment` cycles attached to it that invokes `method` with
205/// arguments `args` on the principal identified by `id`, ignoring the reply.
206///
207/// Returns `Ok(())` if the message was successfully enqueued, otherwise returns a reject code.
208///
209/// # Notes
210///
211/// * The caller has no way of checking whether the destination processed the notification.
212/// The system can drop the notification if the destination does not have resources to
213/// process the message (for example, if it's out of cycles or queue slots).
214///
215/// * The callee cannot tell whether the call is one-way or not.
216/// The callee must produce replies for all incoming messages.
217///
218/// * It is safe to upgrade a canister without stopping it first if it sends out *only*
219/// one-way messages.
220///
221/// * If the payment is non-zero and the system fails to deliver the notification, the behaviour
222/// is unspecified: the funds can be either reimbursed or consumed irrevocably by the IC depending
223/// on the underlying implementation of one-way calls.
224pub fn notify_with_payment128<T: ArgumentEncoder>(
225 id: Principal,
226 method: &str,
227 args: T,
228 payment: u128,
229) -> Result<(), RejectionCode> {
230 let args_raw = encode_args(args).expect("failed to encode arguments");
231 notify_raw(id, method, &args_raw, payment)
232}
233
234/// Like [notify_with_payment128], but sets the payment to zero.
235pub fn notify<T: ArgumentEncoder>(
236 id: Principal,
237 method: &str,
238 args: T,
239) -> Result<(), RejectionCode> {
240 notify_with_payment128(id, method, args, 0)
241}
242
243/// Like [notify], but sends the argument as raw bytes, skipping Candid serialization.
244pub fn notify_raw(
245 id: Principal,
246 method: &str,
247 args_raw: &[u8],
248 payment: u128,
249) -> Result<(), RejectionCode> {
250 let callee = id.as_slice();
251 // We set all callbacks to -1, which is guaranteed to be invalid callback index.
252 // The system will still deliver the reply, but it will trap immediately because the callback
253 // is not a valid function. See
254 // https://www.joachim-breitner.de/blog/789-Zero-downtime_upgrades_of_Internet_Computer_canisters#one-way-calls
255 // for more context.
256
257 // SAFETY:
258 // `callee`, being &[u8], is a readable sequence of bytes and therefore can be passed to ic0.call_new.
259 // `method`, being &str, is a readable sequence of bytes and therefore can be passed to ic0.call_new.
260 // -1, i.e. usize::MAX, is a function pointer the wasm module cannot possibly contain, and therefore can be passed as both reply and reject fn for ic0.call_new.
261 // Since the callback function will never be called, any value can be passed as its context parameter, and therefore -1 can be passed for those values.
262 // `args`, being a &[u8], is a readable sequence of bytes and therefore can be passed to ic0.call_data_append.
263 // ic0.call_perform is always safe to call.
264 let err_code = unsafe {
265 ic0::call_new(
266 callee.as_ptr() as i32,
267 callee.len() as i32,
268 method.as_ptr() as i32,
269 method.len() as i32,
270 /* reply_fun = */ -1,
271 /* reply_env = */ -1,
272 /* reject_fun = */ -1,
273 /* reject_env = */ -1,
274 );
275 add_payment(payment);
276 ic0::call_data_append(args_raw.as_ptr() as i32, args_raw.len() as i32);
277 ic0::call_perform()
278 };
279 match err_code {
280 0 => Ok(()),
281 c => Err(RejectionCode::from(c)),
282 }
283}
284
285/// Performs an asynchronous call to another canister and pay cycles at the same time.
286///
287/// Treats arguments and returns as raw bytes. No data serialization and deserialization is performed.
288///
289/// # Example
290///
291/// It can be called:
292///
293/// ```rust
294/// # use ic_cdk::api::call::call_raw;
295/// # fn callee_canister() -> candid::Principal { unimplemented!() }
296/// async fn call_add_user() -> Vec<u8>{
297/// call_raw(callee_canister(), "add_user", b"abcd", 1_000_000u64).await.unwrap()
298/// }
299/// ```
300pub fn call_raw<'a, T: AsRef<[u8]> + Send + Sync + 'a>(
301 id: Principal,
302 method: &str,
303 args_raw: T,
304 payment: u64,
305) -> impl Future<Output = CallResult<Vec<u8>>> + Send + Sync + 'a {
306 call_raw_internal(id, method, args_raw, payment.into())
307}
308
309/// Performs an asynchronous call to another canister and pay cycles (in `u128`) at the same time.
310///
311/// Treats arguments and returns as raw bytes. No data serialization and deserialization is performed.
312/// # Example
313///
314/// It can be called:
315///
316/// ```rust
317/// # use ic_cdk::api::call::call_raw128;
318/// # fn callee_canister() -> candid::Principal { unimplemented!() }
319/// async fn call_add_user() -> Vec<u8>{
320/// call_raw128(callee_canister(), "add_user", b"abcd", 1_000_000u128).await.unwrap()
321/// }
322/// ```
323pub fn call_raw128<'a, T: AsRef<[u8]> + Send + Sync + 'a>(
324 id: Principal,
325 method: &str,
326 args_raw: T,
327 payment: u128,
328) -> impl Future<Output = CallResult<Vec<u8>>> + Send + Sync + 'a {
329 call_raw_internal(id, method, args_raw, payment)
330}
331
332fn call_raw_internal<'a, T: AsRef<[u8]> + Send + Sync + 'a>(
333 id: Principal,
334 method: &str,
335 args_raw: T,
336 payment: u128,
337) -> impl Future<Output = CallResult<Vec<u8>>> + Send + Sync + 'a {
338 let state = Arc::new(RwLock::new(CallFutureState {
339 result: None,
340 waker: None,
341 id,
342 method: method.to_string(),
343 arg: args_raw,
344 payment,
345 }));
346 CallFuture { state }
347}
348
349fn decoder_error_to_reject<T>(err: candid::error::Error) -> (RejectionCode, String) {
350 (
351 RejectionCode::CanisterError,
352 format!(
353 "failed to decode canister response as {}: {}",
354 std::any::type_name::<T>(),
355 err
356 ),
357 )
358}
359
360/// Performs an asynchronous call to another canister.
361///
362/// # Example
363///
364/// Assuming that the callee canister has following interface:
365///
366/// ```text
367/// service : {
368/// add_user: (name: text) -> (nat64);
369/// }
370/// ```
371///
372/// It can be called:
373///
374/// ```rust
375/// # use ic_cdk::api::call::call;
376/// # fn callee_canister() -> candid::Principal { unimplemented!() }
377/// async fn call_add_user() -> u64 {
378/// let (user_id,) = call(callee_canister(), "add_user", ("Alice".to_string(),)).await.unwrap();
379/// user_id
380/// }
381/// ```
382///
383/// # Note
384///
385/// * Both argument and return types are tuples even if it has only one value, e.g `(user_id,)`, `("Alice".to_string(),)`.
386/// * The type annotation on return type is required. Or the return type can be inferred from the context.
387/// * The asynchronous call must be awaited in order for the inter-canister call to be made.
388/// * If the reply payload is not a valid encoding of the expected type `T`, the call results in [RejectionCode::CanisterError] error.
389pub fn call<T: ArgumentEncoder, R: for<'a> ArgumentDecoder<'a>>(
390 id: Principal,
391 method: &str,
392 args: T,
393) -> impl Future<Output = CallResult<R>> + Send + Sync {
394 let args_raw = encode_args(args).expect("Failed to encode arguments.");
395 let fut = call_raw(id, method, args_raw, 0);
396 async {
397 let bytes = fut.await?;
398 decode_args(&bytes).map_err(decoder_error_to_reject::<R>)
399 }
400}
401
402/// Performs an asynchronous call to another canister and pay cycles at the same time.
403///
404/// # Example
405///
406/// Assuming that the callee canister has following interface:
407///
408/// ```text
409/// service : {
410/// add_user: (name: text) -> (nat64);
411/// }
412/// ```
413///
414/// It can be called:
415///
416/// ```rust
417/// # use ic_cdk::api::call::call_with_payment;
418/// # fn callee_canister() -> candid::Principal { unimplemented!() }
419/// async fn call_add_user() -> u64 {
420/// let (user_id,) = call_with_payment(callee_canister(), "add_user", ("Alice".to_string(),), 1_000_000u64).await.unwrap();
421/// user_id
422/// }
423/// ```
424///
425/// # Note
426///
427/// * Both argument and return types are tuples even if it has only one value, e.g `(user_id,)`, `("Alice".to_string(),)`.
428/// * The type annotation on return type is required. Or the return type can be inferred from the context.
429/// * The asynchronous call must be awaited in order for the inter-canister call to be made.
430/// * If the reply payload is not a valid encoding of the expected type `T`, the call results in [RejectionCode::CanisterError] error.
431pub fn call_with_payment<T: ArgumentEncoder, R: for<'a> ArgumentDecoder<'a>>(
432 id: Principal,
433 method: &str,
434 args: T,
435 cycles: u64,
436) -> impl Future<Output = CallResult<R>> + Send + Sync {
437 let args_raw = encode_args(args).expect("Failed to encode arguments.");
438 let fut = call_raw(id, method, args_raw, cycles);
439 async {
440 let bytes = fut.await?;
441 decode_args(&bytes).map_err(decoder_error_to_reject::<R>)
442 }
443}
444
445/// Performs an asynchronous call to another canister and pay cycles (in `u128`) at the same time.
446///
447/// # Example
448///
449/// Assuming that the callee canister has following interface:
450///
451/// ```text
452/// service : {
453/// add_user: (name: text) -> (nat64);
454/// }
455/// ```
456///
457/// It can be called:
458///
459/// ```rust
460/// # use ic_cdk::api::call::call_with_payment128;
461/// # fn callee_canister() -> candid::Principal { unimplemented!() }
462/// async fn call_add_user() -> u64 {
463/// let (user_id,) = call_with_payment128(callee_canister(), "add_user", ("Alice".to_string(),), 1_000_000u128).await.unwrap();
464/// user_id
465/// }
466/// ```
467///
468/// # Note
469///
470/// * Both argument and return types are tuples even if it has only one value, e.g `(user_id,)`, `("Alice".to_string(),)`.
471/// * The type annotation on return type is required. Or the return type can be inferred from the context.
472/// * The asynchronous call must be awaited in order for the inter-canister call to be made.
473/// * If the reply payload is not a valid encoding of the expected type `T`, the call results in [RejectionCode::CanisterError] error.
474pub fn call_with_payment128<T: ArgumentEncoder, R: for<'a> ArgumentDecoder<'a>>(
475 id: Principal,
476 method: &str,
477 args: T,
478 cycles: u128,
479) -> impl Future<Output = CallResult<R>> + Send + Sync {
480 let args_raw = encode_args(args).expect("Failed to encode arguments.");
481 let fut = call_raw128(id, method, args_raw, cycles);
482 async {
483 let bytes = fut.await?;
484 decode_args(&bytes).map_err(decoder_error_to_reject::<R>)
485 }
486}
487
488/// Performs an asynchronous call to another canister and pay cycles (in `u128`).
489/// It also allows setting a quota for decoding the return values.
490/// The decoding quota is strongly recommended when calling third-party or untrusted canisters.
491///
492/// # Example
493///
494/// Assuming that the callee canister has following interface:
495///
496/// ```text
497/// service : {
498/// add_user: (name: text) -> (nat64);
499/// }
500/// ```
501///
502/// It can be called:
503///
504/// ```rust
505/// # use ic_cdk::api::call::{call_with_config, ArgDecoderConfig};
506/// # fn callee_canister() -> candid::Principal { unimplemented!() }
507/// async fn call_add_user() -> u64 {
508/// let config = ArgDecoderConfig {
509/// // The function only returns a nat64, to accomodate future upgrades, we set a larger decoding_quota.
510/// decoding_quota: Some(10_000),
511/// // To accomodate future upgrades, reserve some skipping_quota.
512/// skipping_quota: Some(100),
513/// // Enable debug mode to print decoding instructions and cost to the replica log.
514/// debug: true,
515/// };
516/// let (user_id,) = call_with_config(callee_canister(), "add_user", ("Alice".to_string(),), 1_000_000u128, &config).await.unwrap();
517/// user_id
518/// }
519/// ```
520pub fn call_with_config<'b, T: ArgumentEncoder, R: for<'a> ArgumentDecoder<'a>>(
521 id: Principal,
522 method: &'b str,
523 args: T,
524 cycles: u128,
525 arg_config: &'b ArgDecoderConfig,
526) -> impl Future<Output = CallResult<R>> + Send + Sync + 'b {
527 let args_raw = encode_args(args).expect("Failed to encode arguments.");
528 let fut = call_raw128(id, method, args_raw, cycles);
529 async move {
530 let bytes = fut.await?;
531 let config = arg_config.to_candid_config();
532 let pre_cycles = if arg_config.debug {
533 Some(crate::api::performance_counter(0))
534 } else {
535 None
536 };
537 match decode_args_with_config_debug(&bytes, &config) {
538 Err(e) => Err(decoder_error_to_reject::<R>(e)),
539 Ok((r, cost)) => {
540 if arg_config.debug {
541 print_decoding_debug_info(&format!("{method} return"), &cost, pre_cycles);
542 }
543 Ok(r)
544 }
545 }
546 }
547}
548
549fn print_decoding_debug_info(title: &str, cost: &DecoderConfig, pre_cycles: Option<u64>) {
550 use crate::api::{performance_counter, print};
551 let pre_cycles = pre_cycles.unwrap_or(0);
552 let instrs = performance_counter(0) - pre_cycles;
553 print(format!("[Debug] {title} decoding instructions: {instrs}"));
554 if let Some(n) = cost.decoding_quota {
555 print(format!("[Debug] {title} decoding cost: {n}"));
556 }
557 if let Some(n) = cost.skipping_quota {
558 print(format!("[Debug] {title} skipping cost: {n}"));
559 }
560}
561
562/// Returns a result that maps over the call
563///
564/// It will be Ok(T) if the call succeeded (with T being the arg_data),
565/// and [reject_message()] if it failed.
566pub fn result<T: for<'a> ArgumentDecoder<'a>>() -> Result<T, String> {
567 match reject_code() {
568 RejectionCode::NoError => {
569 decode_args(&arg_data_raw()).map_err(|e| format!("Failed to decode arguments: {}", e))
570 }
571 _ => Err(reject_message()),
572 }
573}
574
575/// Returns the rejection code for the call.
576pub fn reject_code() -> RejectionCode {
577 // SAFETY: ic0.msg_reject_code is always safe to call.
578 let code = unsafe { ic0::msg_reject_code() };
579 RejectionCode::from(code)
580}
581
582/// Returns the rejection message.
583pub fn reject_message() -> String {
584 // SAFETY: ic0.msg_reject_msg_size is always safe to call.
585 let len: u32 = unsafe { ic0::msg_reject_msg_size() as u32 };
586 let mut bytes = vec![0u8; len as usize];
587 // SAFETY: `bytes`, being mutable and allocated to `len` bytes, is safe to pass to ic0.msg_reject_msg_copy with no offset
588 unsafe {
589 ic0::msg_reject_msg_copy(bytes.as_mut_ptr() as i32, 0, len as i32);
590 }
591 String::from_utf8_lossy(&bytes).into_owned()
592}
593
594/// Rejects the current call with the message.
595pub fn reject(message: &str) {
596 let err_message = message.as_bytes();
597 // SAFETY: `err_message`, being &[u8], is a readable sequence of bytes, and therefore valid to pass to ic0.msg_reject.
598 unsafe {
599 ic0::msg_reject(err_message.as_ptr() as i32, err_message.len() as i32);
600 }
601}
602
603/// An io::Write for message replies.
604#[derive(Debug, Copy, Clone)]
605pub struct CallReplyWriter;
606
607impl std::io::Write for CallReplyWriter {
608 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
609 // SAFETY: buf, being &[u8], is a readable sequence of bytes, and therefore valid to pass to ic0.msg_reply_data_append.
610 unsafe {
611 ic0::msg_reply_data_append(buf.as_ptr() as i32, buf.len() as i32);
612 }
613 Ok(buf.len())
614 }
615
616 fn flush(&mut self) -> std::io::Result<()> {
617 Ok(())
618 }
619}
620
621/// Replies to the current call with a candid argument.
622pub fn reply<T: ArgumentEncoder>(reply: T) {
623 write_args(&mut CallReplyWriter, reply).expect("Could not encode reply.");
624 // SAFETY: ic0.msg_reply is always safe to call.
625 unsafe {
626 ic0::msg_reply();
627 }
628}
629
630/// Returns the amount of cycles that were transferred by the caller
631/// of the current call, and is still available in this message.
632pub fn msg_cycles_available() -> u64 {
633 // SAFETY: ic0.msg_cycles_available is always safe to call.
634 unsafe { ic0::msg_cycles_available() as u64 }
635}
636
637/// Returns the amount of cycles that were transferred by the caller
638/// of the current call, and is still available in this message.
639pub fn msg_cycles_available128() -> u128 {
640 let mut recv = 0u128;
641 // SAFETY: recv is writable and sixteen bytes wide, and therefore is safe to pass to ic0.msg_cycles_available128
642 unsafe {
643 ic0::msg_cycles_available128(&mut recv as *mut u128 as i32);
644 }
645 recv
646}
647
648/// Returns the amount of cycles that came back with the response as a refund.
649///
650/// The refund has already been added to the canister balance automatically.
651pub fn msg_cycles_refunded() -> u64 {
652 // SAFETY: ic0.msg_cycles_refunded is always safe to call
653 unsafe { ic0::msg_cycles_refunded() as u64 }
654}
655
656/// Returns the amount of cycles that came back with the response as a refund.
657///
658/// The refund has already been added to the canister balance automatically.
659pub fn msg_cycles_refunded128() -> u128 {
660 let mut recv = 0u128;
661 // SAFETY: recv is writable and sixteen bytes wide, and therefore is safe to pass to ic0.msg_cycles_refunded128
662 unsafe {
663 ic0::msg_cycles_refunded128(&mut recv as *mut u128 as i32);
664 }
665 recv
666}
667
668/// Moves cycles from the call to the canister balance.
669///
670/// The actual amount moved will be returned.
671pub fn msg_cycles_accept(max_amount: u64) -> u64 {
672 // SAFETY: ic0.msg_cycles_accept is always safe to call.
673 unsafe { ic0::msg_cycles_accept(max_amount as i64) as u64 }
674}
675
676/// Moves cycles from the call to the canister balance.
677///
678/// The actual amount moved will be returned.
679pub fn msg_cycles_accept128(max_amount: u128) -> u128 {
680 let high = (max_amount >> 64) as u64;
681 let low = (max_amount & u64::MAX as u128) as u64;
682 let mut recv = 0u128;
683 // SAFETY: `recv` is writable and sixteen bytes wide, and therefore safe to pass to ic0.msg_cycles_accept128
684 unsafe {
685 ic0::msg_cycles_accept128(high as i64, low as i64, &mut recv as *mut u128 as i32);
686 }
687 recv
688}
689
690/// Returns the argument data as bytes.
691pub fn arg_data_raw() -> Vec<u8> {
692 // SAFETY: ic0.msg_arg_data_size is always safe to call.
693 let len: usize = unsafe { ic0::msg_arg_data_size() as usize };
694 let mut bytes = Vec::with_capacity(len);
695 // SAFETY:
696 // `bytes`, being mutable and allocated to `len` bytes, is safe to pass to ic0.msg_arg_data_copy with no offset
697 // ic0.msg_arg_data_copy writes to all of `bytes[0..len]`, so `set_len` is safe to call with the new len.
698 unsafe {
699 ic0::msg_arg_data_copy(bytes.as_mut_ptr() as i32, 0, len as i32);
700 bytes.set_len(len);
701 }
702 bytes
703}
704
705/// Gets the len of the raw-argument-data-bytes.
706pub fn arg_data_raw_size() -> usize {
707 // SAFETY: ic0.msg_arg_data_size is always safe to call.
708 unsafe { ic0::msg_arg_data_size() as usize }
709}
710
711/// Replies with the bytes passed
712pub fn reply_raw(buf: &[u8]) {
713 if !buf.is_empty() {
714 // SAFETY: `buf`, being &[u8], is a readable sequence of bytes, and therefore valid to pass to ic0.msg_reject.
715 unsafe { ic0::msg_reply_data_append(buf.as_ptr() as i32, buf.len() as i32) }
716 };
717 // SAFETY: ic0.msg_reply is always safe to call.
718 unsafe { ic0::msg_reply() };
719}
720
721#[derive(Debug)]
722/// Config to control the behavior of decoding canister endpoint arguments.
723pub struct ArgDecoderConfig {
724 /// Limit the total amount of work the deserializer can perform. See [docs on the Candid library](https://docs.rs/candid/latest/candid/de/struct.DecoderConfig.html#method.set_decoding_quota) to understand the cost model.
725 pub decoding_quota: Option<usize>,
726 /// Limit the total amount of work for skipping unneeded data on the wire. See [docs on the Candid library](https://docs.rs/candid/latest/candid/de/struct.DecoderConfig.html#method.set_skipping_quota) to understand the skipping cost.
727 pub skipping_quota: Option<usize>,
728 /// When set to true, print instruction count and the decoding/skipping cost to the replica log.
729 pub debug: bool,
730}
731impl ArgDecoderConfig {
732 fn to_candid_config(&self) -> DecoderConfig {
733 let mut config = DecoderConfig::new();
734 if let Some(n) = self.decoding_quota {
735 config.set_decoding_quota(n);
736 }
737 if let Some(n) = self.skipping_quota {
738 config.set_skipping_quota(n);
739 }
740 if self.debug {
741 config.set_full_error_message(true);
742 }
743 config
744 }
745}
746impl Default for ArgDecoderConfig {
747 fn default() -> Self {
748 Self {
749 decoding_quota: None,
750 skipping_quota: Some(10_000),
751 debug: false,
752 }
753 }
754}
755
756/// Returns the argument data in the current call. Traps if the data cannot be
757/// decoded.
758pub fn arg_data<R: for<'a> ArgumentDecoder<'a>>(arg_config: ArgDecoderConfig) -> R {
759 let bytes = arg_data_raw();
760
761 let config = arg_config.to_candid_config();
762 let res = decode_args_with_config_debug(&bytes, &config);
763 match res {
764 Err(e) => trap(&format!("failed to decode call arguments: {:?}", e)),
765 Ok((r, cost)) => {
766 if arg_config.debug {
767 print_decoding_debug_info("Argument", &cost, None);
768 }
769 r
770 }
771 }
772}
773
774/// Accepts the ingress message.
775pub fn accept_message() {
776 // SAFETY: ic0.accept_message is always safe to call.
777 unsafe {
778 ic0::accept_message();
779 }
780}
781
782/// Returns the name of current canister method.
783pub fn method_name() -> String {
784 // SAFETY: ic0.msg_method_name_size is always safe to call.
785 let len: u32 = unsafe { ic0::msg_method_name_size() as u32 };
786 let mut bytes = vec![0u8; len as usize];
787 // SAFETY: `bytes` is writable and allocated to `len` bytes, and therefore can be safely passed to ic0.msg_method_name_copy
788 unsafe {
789 ic0::msg_method_name_copy(bytes.as_mut_ptr() as i32, 0, len as i32);
790 }
791 String::from_utf8_lossy(&bytes).into_owned()
792}
793
794/// Gets the value of specified performance counter
795///
796/// See [`crate::api::performance_counter`].
797#[deprecated(
798 since = "0.11.3",
799 note = "This method conceptually doesn't belong to this module. Please use `ic_cdk::api::performance_counter` instead."
800)]
801pub fn performance_counter(counter_type: u32) -> u64 {
802 // SAFETY: ic0.performance_counter is always safe to call.
803 unsafe { ic0::performance_counter(counter_type as i32) as u64 }
804}
805
806/// Pretends to have the Candid type `T`, but unconditionally errors
807/// when serialized.
808///
809/// Usable, but not required, as metadata when using `#[query(manual_reply = true)]`,
810/// so an accurate Candid file can still be generated.
811#[derive(Debug, Copy, Clone, Default)]
812pub struct ManualReply<T: ?Sized>(PhantomData<T>);
813
814impl<T: ?Sized> ManualReply<T> {
815 /// Constructs a new `ManualReply`.
816 #[allow(clippy::self_named_constructors)]
817 pub const fn empty() -> Self {
818 Self(PhantomData)
819 }
820 /// Replies with the given value and returns a new `ManualReply`,
821 /// for a useful reply-then-return shortcut.
822 pub fn all<U>(value: U) -> Self
823 where
824 U: ArgumentEncoder,
825 {
826 reply(value);
827 Self::empty()
828 }
829 /// Replies with a one-element tuple around the given value and returns
830 /// a new `ManualReply`, for a useful reply-then-return shortcut.
831 pub fn one<U>(value: U) -> Self
832 where
833 U: CandidType,
834 {
835 reply((value,));
836 Self::empty()
837 }
838
839 /// Rejects the call with the specified message and returns a new
840 /// `ManualReply`, for a useful reply-then-return shortcut.
841 pub fn reject(message: impl AsRef<str>) -> Self {
842 reject(message.as_ref());
843 Self::empty()
844 }
845}
846
847impl<T> CandidType for ManualReply<T>
848where
849 T: CandidType + ?Sized,
850{
851 fn _ty() -> candid::types::Type {
852 T::_ty()
853 }
854 /// Unconditionally errors.
855 fn idl_serialize<S>(&self, _: S) -> Result<(), S::Error>
856 where
857 S: candid::types::Serializer,
858 {
859 Err(S::Error::custom("`Empty` cannot be serialized"))
860 }
861}
862
863/// Tells you whether the current async fn is being canceled due to a trap/panic.
864///
865/// If a function traps/panics, then the canister state is rewound to the beginning of the function.
866/// However, due to the way async works, the beginning of the function as the IC understands it is actually
867/// the most recent `await` from an inter-canister-call. This means that part of the function will have executed,
868/// and part of it won't.
869///
870/// When this happens the CDK will cancel the task, causing destructors to be run. If you need any functions to be run
871/// no matter what happens, they should happen in a destructor; the [`scopeguard`](https://docs.rs/scopeguard) crate
872/// provides a convenient wrapper for this. In a destructor, `is_recovering_from_trap` serves the same purpose as
873/// [std::thread::panicking] - it tells you whether the destructor is executing *because* of a trap,
874/// as opposed to just because the scope was exited, so you could e.g. implement mutex poisoning.
875pub fn is_recovering_from_trap() -> bool {
876 ic_cdk_executor::CLEANUP.load(Ordering::Relaxed)
877}