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