ic_cdk/api/
call.rs

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