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