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}