ic0/
lib.rs

1//! Bindings to the [Internet Computer system API](https://internetcomputer.org/docs/current/references/ic-interface-spec#system-api-imports).
2//!
3//! The raw bindings can be found in the [`sys`] module. The functions in the crate root provide slightly higher-level
4//! bindings in terms of slices instead of pointers/lengths, accurately typed pointers, etc., but otherwise does not adapt
5//! the API. Where this is all that is needed for the functions to be safe, they are marked as safe, but function pointers
6//! cannot be made safe and as such `call_new` is still unsafe.
7//!
8//! Any function `ic0.foo` that would write to a user buffer has two versions, `foo` which takes `&mut [u8]` and
9//! `foo_uninit` which takes `&mut [MaybeUninit<u8>]`.
10
11#![warn(
12    elided_lifetimes_in_paths,
13    missing_debug_implementations,
14    unsafe_op_in_unsafe_fn,
15    clippy::undocumented_unsafe_blocks,
16    clippy::missing_safety_doc
17)]
18
19use std::mem::MaybeUninit;
20
21pub mod sys;
22
23#[inline]
24pub fn msg_arg_data_size() -> usize {
25    // SAFETY: ic0.msg_arg_data_size is always safe to call
26    unsafe { sys::msg_arg_data_size() }
27}
28
29#[inline]
30pub fn msg_arg_data_copy(dst: &mut [u8], offset: usize) {
31    // SAFETY: dst is a writable sequence of bytes and therefore safe to pass as ptr and len to ic0.msg_arg_data_copy
32    // The offset parameter does not affect safety
33    unsafe { sys::msg_arg_data_copy(dst.as_mut_ptr() as usize, offset, dst.len()) }
34}
35
36/// # Safety
37///
38/// This function will fully initialize `dst` (or trap if it cannot).
39#[inline]
40pub fn msg_arg_data_copy_uninit(dst: &mut [MaybeUninit<u8>], offset: usize) {
41    // SAFETY: dst is a writable sequence of bytes and therefore safe to pass as ptr and len to ic0.msg_arg_data_copy
42    // The offset parameter does not affect safety
43    unsafe { sys::msg_arg_data_copy(dst.as_mut_ptr() as usize, offset, dst.len()) }
44}
45
46#[inline]
47pub fn msg_caller_size() -> usize {
48    // SAFETY: ic0.msg_caller_size is always safe to call
49    unsafe { sys::msg_caller_size() }
50}
51
52#[inline]
53pub fn msg_caller_copy(dst: &mut [u8], offset: usize) {
54    // SAFETY: dst is a writable sequence of bytes and therefore safe to pass as ptr and len to ic0.msg_caller_copy
55    // The offset parameter does not affect safety
56    unsafe { sys::msg_caller_copy(dst.as_mut_ptr() as usize, offset, dst.len()) }
57}
58
59/// # Safety
60///
61/// This function will fully initialize `dst` (or trap if it cannot).
62#[inline]
63pub fn msg_caller_copy_uninit(dst: &mut [MaybeUninit<u8>], offset: usize) {
64    // SAFETY: dst is a writable sequence of bytes and therefore safe to pass as ptr and len to ic0.msg_caller_copy
65    // The offset parameter does not affect safety
66    unsafe { sys::msg_caller_copy(dst.as_mut_ptr() as usize, offset, dst.len()) }
67}
68
69#[inline]
70pub fn msg_reject_code() -> u32 {
71    // SAFETY: ic0.msg_reject_code is always safe to call
72    unsafe { sys::msg_reject_code() }
73}
74
75#[inline]
76pub fn msg_reject_msg_size() -> usize {
77    // SAFETY: ic0.msg_reject_msg_size is always safe to call
78    unsafe { sys::msg_reject_msg_size() }
79}
80
81#[inline]
82pub fn msg_reject_msg_copy(dst: &mut [u8], offset: usize) {
83    // SAFETY: dst is a writable sequence of bytes and therefore safe to pass as ptr and len to ic0.msg_reject_msg_copy
84    // The offset parameter does not affect safety
85    unsafe { sys::msg_reject_msg_copy(dst.as_mut_ptr() as usize, offset, dst.len()) }
86}
87
88/// # Safety
89///
90/// This function will fully initialize `dst` (or trap if it cannot).
91#[inline]
92pub fn msg_reject_msg_copy_uninit(dst: &mut [MaybeUninit<u8>], offset: usize) {
93    // SAFETY: dst is a writable sequence of bytes and therefore safe to pass as ptr and len to ic0.msg_reject_msg_copy
94    // The offset parameter does not affect safety
95    unsafe { sys::msg_reject_msg_copy(dst.as_mut_ptr() as usize, offset, dst.len()) }
96}
97
98#[inline]
99pub fn msg_deadline() -> u64 {
100    // SAFETY: ic0.msg_deadline is always safe to call
101    unsafe { sys::msg_deadline() }
102}
103
104#[inline]
105pub fn msg_reply_data_append(data: &[u8]) {
106    // SAFETY: data is a readable sequence of bytes and therefore safe to pass as ptr and len to ic0.msg_reply_data_append
107    unsafe { sys::msg_reply_data_append(data.as_ptr() as usize, data.len()) }
108}
109
110#[inline]
111pub fn msg_reply() {
112    // SAFETY: ic0.msg_reply is always safe to call
113    unsafe { sys::msg_reply() }
114}
115
116#[inline]
117pub fn msg_reject(message: &[u8]) {
118    // SAFETY: message is a readable sequence of bytes and therefore safe to pass as ptr and len to ic0.msg_reject
119    unsafe { sys::msg_reject(message.as_ptr() as usize, message.len()) }
120}
121
122#[inline]
123pub fn msg_cycles_available128() -> u128 {
124    let mut dst_bytes = [0_u8; 16];
125    // SAFETY: dst_bytes is a writable sequence of 16 bytes and therefore safe to pass as ptr to ic0.msg_cycles_available128
126    unsafe {
127        sys::msg_cycles_available128(dst_bytes.as_mut_ptr() as usize);
128    }
129    u128::from_le_bytes(dst_bytes)
130}
131
132#[inline]
133pub fn msg_cycles_refunded128() -> u128 {
134    let mut dst_bytes = [0_u8; 16];
135    // SAFETY: dst_bytes is a writable sequence of 16 bytes and therefore safe to pass as ptr to ic0.msg_cycles_refunded128
136    unsafe {
137        sys::msg_cycles_refunded128(dst_bytes.as_mut_ptr() as usize);
138    }
139    u128::from_le_bytes(dst_bytes)
140}
141
142#[inline]
143pub fn msg_cycles_accept128(max: u128) -> u128 {
144    let (high, low) = to_high_low(max);
145    let mut dst_bytes = [0_u8; 16];
146    // SAFETY: dst_bytes is a writable sequence of 16 bytes and therefore safe to pass as ptr to ic0.msg_cycles_accept128
147    // The max_amount_high and max_amount_low parameters do not affect safety
148    unsafe {
149        sys::msg_cycles_accept128(high, low, dst_bytes.as_mut_ptr() as usize);
150    }
151    u128::from_le_bytes(dst_bytes)
152}
153
154#[inline]
155pub fn cycles_burn128(amount: u128) -> u128 {
156    let (high, low) = to_high_low(amount);
157    let mut dst_bytes = [0_u8; 16];
158    // SAFETY: dst_bytes is a writable sequence of 16 bytes and therefore safe to pass to ic0.cycles_burn128
159    // The amount_high and amount_low parameters do not affect safety
160    unsafe {
161        sys::cycles_burn128(high, low, dst_bytes.as_mut_ptr() as usize);
162    }
163    u128::from_le_bytes(dst_bytes)
164}
165
166#[inline]
167pub fn canister_self_size() -> usize {
168    // SAFETY: ic0.canister_self_size is always safe to call
169    unsafe { sys::canister_self_size() }
170}
171
172#[inline]
173pub fn canister_self_copy(dst: &mut [u8], offset: usize) {
174    // SAFETY: dst is a writable sequence of bytes and therefore safe to pass to ic0.canister_self_copy
175    // The offset parameter does not affect safety
176    unsafe { sys::canister_self_copy(dst.as_mut_ptr() as usize, offset, dst.len()) }
177}
178
179#[inline]
180pub fn canister_cycle_balance128() -> u128 {
181    let mut dst_bytes = [0_u8; 16];
182    // SAFETY: dst_bytes is a writable sequence of 16 bytes and therefore safe to pass to ic0.canister_cycle_balance128
183    unsafe {
184        sys::canister_cycle_balance128(dst_bytes.as_mut_ptr() as usize);
185    }
186    u128::from_le_bytes(dst_bytes)
187}
188
189#[inline]
190pub fn canister_liquid_cycle_balance128() -> u128 {
191    let mut dst_bytes = [0_u8; 16];
192    // SAFETY: dst_bytes is a writable sequence of 16 bytes and therefore safe to pass to ic0.canister_liquid_cycle_balance128
193    unsafe {
194        sys::canister_liquid_cycle_balance128(dst_bytes.as_mut_ptr() as usize);
195    }
196    u128::from_le_bytes(dst_bytes)
197}
198
199#[inline]
200pub fn canister_status() -> u32 {
201    // SAFETY: ic0.canister_status is always safe to call.
202    unsafe { sys::canister_status() }
203}
204
205#[inline]
206pub fn canister_version() -> u64 {
207    // SAFETY: ic0.canister_version is always safe to call.
208    unsafe { sys::canister_version() }
209}
210
211#[inline]
212pub fn subnet_self_size() -> usize {
213    // SAFETY: ic0.subnet_self_size is always safe to call.
214    unsafe { sys::subnet_self_size() }
215}
216
217#[inline]
218pub fn subnet_self_copy(dst: &mut [u8], offset: usize) {
219    // SAFETY: dst is a writable sequence of bytes and therefore safe to pass as ptr and len to ic0.subnet_self_copy
220    // The offset parameter does not affect safety
221    unsafe { sys::subnet_self_copy(dst.as_mut_ptr() as usize, offset, dst.len()) }
222}
223
224/// # Safety
225///
226/// This function will fully initialize `dst` (or trap if it cannot).
227#[inline]
228pub fn subnet_self_copy_uninit(dst: &mut [MaybeUninit<u8>], offset: usize) {
229    // SAFETY: dst is a writable sequence of bytes and therefore safe to pass as ptr and len to ic0.subnet_self_copy
230    // The offset parameter does not affect safety
231    unsafe { sys::subnet_self_copy(dst.as_mut_ptr() as usize, offset, dst.len()) }
232}
233
234#[inline]
235pub fn msg_method_name_size() -> usize {
236    // SAFETY: ic0.msg_method_name_size is always safe to call
237    unsafe { sys::msg_method_name_size() }
238}
239
240#[inline]
241pub fn msg_method_name_copy(dst: &mut [u8], offset: usize) {
242    // SAFETY: dst is a writable sequence of bytes and therefore safe to pass as ptr and len to ic0.msg_method_name_copy
243    // The offset parameter does not affect safety
244    unsafe { sys::msg_method_name_copy(dst.as_mut_ptr() as usize, offset, dst.len()) }
245}
246
247/// # Safety
248///
249/// This function will fully initialize `dst` (or trap if it cannot).
250#[inline]
251pub fn msg_method_name_copy_uninit(dst: &mut [MaybeUninit<u8>], offset: usize) {
252    // SAFETY: dst is a writable sequence of bytes and therefore safe to pass as ptr and len to ic0.msg_method_name_copy
253    // The offset parameter does not affect safety
254    unsafe { sys::msg_method_name_copy(dst.as_mut_ptr() as usize, offset, dst.len()) }
255}
256
257#[inline]
258pub fn accept_message() {
259    // SAFETY: ic0.accept_message is always safe to call
260    unsafe { sys::accept_message() }
261}
262
263/// # Safety
264///
265/// - `reply_fn` is required to be safely callable as a canister entrypoint with `reply_env`.
266/// - `reject_fn` is required to be safely callable as a canister entrypoint with `reject_env`.
267/// - Ownership of `reply_env` and `reject_env` is acquired by this function.
268/// - `reply_fn`, if called, will receive ownership of `reply_env`, `reject_env`, and [`cleanup_env`](call_on_cleanup).
269/// - `reject_fn`, if called, will receive ownership of `reply_env`, `reject_env`, and [`cleanup_env`](call_on_cleanup).
270#[inline]
271pub unsafe fn call_new(
272    callee: &[u8],
273    name: &str,
274    reply_fn: unsafe extern "C" fn(env: usize),
275    reply_env: usize,
276    reject_fn: unsafe extern "C" fn(env: usize),
277    reject_env: usize,
278) {
279    // SAFETY:
280    // - callee, being &[u8], is a readable sequence of bytes and therefore safe to pass as ptr and len
281    //   as the callee in ic0.call_new
282    // - name is a readable string and therefore safe to pass as ptr and len as the name in ic0.call_new
283    // - reply_fn is a function with signature (env : usize) -> () and required to be a safe entrypoint if reply_env is used
284    //   as the env, and is therefore safe to pass as the reply fn for ic0.call_new if reply_env is passed as the reply env
285    // - reply_env is the correct env parameter for reply_fn
286    // - reject_fn is a function with signature (env : usize) -> () and required to be a safe entrypoint if reject_env is used
287    //   as the env, and is therefore safe to pass as the reject fn for ic0.call_new if reject_env is passed as the reject env
288    // - reject_env is the correct env parameter for reject_fn
289    unsafe {
290        sys::call_new(
291            callee.as_ptr() as usize,
292            callee.len(),
293            name.as_ptr() as usize,
294            name.len(),
295            reply_fn as usize,
296            reply_env,
297            reject_fn as usize,
298            reject_env,
299        );
300    }
301}
302
303#[inline]
304pub fn call_new_oneway(callee: &[u8], name: &str) {
305    // SAFETY:
306    // - callee, being &[u8], is a readable sequence of bytes and therefore safe to pass as ptr and len
307    //   as the callee in ic0.call_new
308    // - name is a readable string and therefore safe to pass as ptr and len as the name in ic0.call_new
309    // - `usize::MAX` is a function pointer the wasm module cannot possibly contain and is therefore safe to pass as
310    //   `reply_fun` and `reject_fun` to ic0.call_new
311    // - When the `reply_fun` and `reject_fun` functions do not exist and therefore will never be called, any value
312    //   is safe to pass as `reply_env` and `reject_env` to `ic0.call_new`
313    //
314    // See https://www.joachim-breitner.de/blog/789-Zero-downtime_upgrades_of_Internet_Computer_canisters#one-way-calls for more context.
315    unsafe {
316        sys::call_new(
317            callee.as_ptr() as usize,
318            callee.len(),
319            name.as_ptr() as usize,
320            name.len(),
321            usize::MAX,
322            usize::MAX,
323            usize::MAX,
324            usize::MAX,
325        );
326    }
327}
328
329/// # Safety
330///
331/// - `cleanup_fn` is required to be safely callable as a canister entrypoint with `cleanup_env`.
332/// - Ownership of `cleanup_env` is acquired by this function.
333/// - `cleanup_fn`, if called, will receive ownership of `cleanup_env`, [`reply_env`](call_new), and [`reject_env`](call_new)
334#[inline]
335pub unsafe fn call_on_cleanup(cleanup_fn: unsafe extern "C" fn(env: usize), cleanup_env: usize) {
336    // SAFETY:
337    // - cleanup_fn is a function with signature (env : usize) -> () and required to be a safe entrypoint if cleanup_env is used
338    //   as the env, and is therefore safe to pass as the fn for ic0.call_on_cleanup if cleanup_env is passed as the env
339    // - cleanup_env is the correct env parameter for cleanup_fn
340    unsafe {
341        sys::call_on_cleanup(cleanup_fn as usize, cleanup_env);
342    }
343}
344
345#[inline]
346pub fn call_data_append(data: &[u8]) {
347    // SAFETY: data is a readable sequence of bytes and therefore safe to pass as ptr and len to ic0.call_data_append
348    unsafe { sys::call_data_append(data.as_ptr() as usize, data.len()) }
349}
350
351#[inline]
352pub fn call_with_best_effort_response(timeout_seconds: u32) {
353    // SAFETY: ic0.call_with_best_effort_response is always safe to call
354    unsafe { sys::call_with_best_effort_response(timeout_seconds) }
355}
356
357#[inline]
358pub fn call_cycles_add128(amount: u128) {
359    let (high, low) = to_high_low(amount);
360    // SAFETY: ic0.call_cycles_add128 is always safe to call
361    unsafe { sys::call_cycles_add128(high, low) }
362}
363
364/// # Safety
365///
366/// If `call_perform` returns a nonzero value, the ownership of [`reply_env`](call_new), [`reject_env`](call_new), and
367/// [`cleanup_env`](call_on_cleanup) is released to the caller.
368///
369/// If `call_perform` returns 0, then (from the perspective of safety, *not* semantics) exactly one of
370/// [`reply_fn`](call_new), [`reject_fn`](call_new), or [`cleanup_fn`](call_on_cleanup) will be called, exactly once.
371#[inline]
372pub fn call_perform() -> u32 {
373    // SAFETY: ic0.call_perform is always safe to call
374    unsafe { sys::call_perform() }
375}
376
377#[inline]
378pub fn stable64_size() -> u64 {
379    // SAFETY: ic0.stable64_size is always safe to call
380    unsafe { sys::stable64_size() }
381}
382
383#[inline]
384pub fn stable64_grow(new_pages: u64) -> u64 {
385    // SAFETY: ic0.stable64_grow is always safe to call
386    unsafe { sys::stable64_grow(new_pages) }
387}
388
389#[inline]
390pub fn stable64_write(data: &[u8], offset: u64) {
391    // SAFETY: data is a readable sequence of bytes and therefore is safe to pass as ptr and len to ic0.stable64_write
392    // The offset parameter does not affect safety
393    unsafe { sys::stable64_write(offset, data.as_ptr() as usize as u64, data.len() as u64) }
394}
395
396#[inline]
397pub fn stable64_read(dst: &mut [u8], offset: u64) {
398    // SAFETY: dst is a writable sequence of bytes and therefore is safe to pass as ptr and len to ic0.stable64_read
399    // The offset parameter does not affect safety
400    unsafe { sys::stable64_read(dst.as_mut_ptr() as usize as u64, offset, dst.len() as u64) }
401}
402
403/// # Safety
404///
405/// This function will fully initialize `dst` (or trap if it cannot).
406#[inline]
407pub fn stable64_read_uninit(dst: &mut [MaybeUninit<u8>], offset: u64) {
408    // SAFETY: dst is a writable sequence of bytes and therefore is safe to pass as ptr and len to ic0.stable64_read
409    // The offset parameter does not affect safety
410    unsafe { sys::stable64_read(dst.as_mut_ptr() as usize as u64, offset, dst.len() as u64) }
411}
412
413#[inline]
414pub fn root_key_size() -> usize {
415    // SAFETY: ic0.root_key_size is always safe to call
416    unsafe { sys::root_key_size() }
417}
418
419#[inline]
420pub fn root_key_copy(dst: &mut [u8], offset: usize) {
421    // SAFETY: dst is a writable sequence of bytes and therefore safe to pass as ptr and len to ic0.root_key_copy
422    // The offset parameter does not affect safety
423    unsafe { sys::root_key_copy(dst.as_mut_ptr() as usize, offset, dst.len()) }
424}
425
426/// # Safety
427///
428/// This function will fully initialize `dst` (or trap if it cannot).
429#[inline]
430pub fn root_key_copy_uninit(dst: &mut [MaybeUninit<u8>], offset: usize) {
431    // SAFETY: dst is a writable sequence of bytes and therefore safe to pass as ptr and len to ic0.root_key_copy
432    // The offset parameter does not affect safety
433    unsafe { sys::root_key_copy(dst.as_mut_ptr() as usize, offset, dst.len()) }
434}
435
436#[inline]
437pub fn certified_data_set(data: &[u8]) {
438    // SAFETY: data is a readable sequence of bytes and therefore safe to pass as ptr and len to ic0.certified_data_set
439    unsafe { sys::certified_data_set(data.as_ptr() as usize, data.len()) }
440}
441
442#[inline]
443pub fn data_certificate_present() -> u32 {
444    // SAFETY: ic0.data_certificate_present is always safe to call
445    unsafe { sys::data_certificate_present() }
446}
447
448#[inline]
449pub fn data_certificate_size() -> usize {
450    // SAFETY: ic0.data_certificate_size is always safe to call
451    unsafe { sys::data_certificate_size() }
452}
453
454#[inline]
455pub fn data_certificate_copy(dst: &mut [u8], offset: usize) {
456    // SAFETY: dst, being &mut [u8], is a writable sequence of bytes and therefore safe to pass as ptr and len to ic0.data_certificate_copy
457    // The offset parameter does not affect safety
458    unsafe { sys::data_certificate_copy(dst.as_mut_ptr() as usize, offset, dst.len()) }
459}
460
461/// # Safety
462///
463/// This function will fully initialize `dst` (or trap if it cannot).
464#[inline]
465pub fn data_certificate_copy_uninit(dst: &mut [MaybeUninit<u8>], offset: usize) {
466    // SAFETY: dst, being &mut [u8], is a writable sequence of bytes and therefore safe to pass as ptr and len to ic0.data_certificate_copy
467    // The offset parameter does not affect safety
468    unsafe { sys::data_certificate_copy(dst.as_mut_ptr() as usize, offset, dst.len()) }
469}
470
471#[inline]
472pub fn time() -> u64 {
473    // SAFETY: ic0.time is always safe to call
474    unsafe { sys::time() }
475}
476
477#[inline]
478pub fn global_timer_set(timestamp: u64) -> u64 {
479    // SAFETY: ic0.global_timer_set is always safe to call
480    unsafe { sys::global_timer_set(timestamp) }
481}
482
483#[inline]
484pub fn performance_counter(counter_type: u32) -> u64 {
485    // SAFETY: ic0.performance_counter is always safe to call
486    unsafe { sys::performance_counter(counter_type) }
487}
488
489#[inline]
490pub fn is_controller(principal: &[u8]) -> u32 {
491    // SAFETY: principal is a readable sequence of bytes and therefore safe to pass as ptr and len to ic0.is_controller
492    unsafe { sys::is_controller(principal.as_ptr() as usize, principal.len()) }
493}
494
495#[inline]
496pub fn in_replicated_execution() -> u32 {
497    // SAFETY: ic0.in_replicated_execution is always safe to call
498    unsafe { sys::in_replicated_execution() }
499}
500
501#[inline]
502pub fn cost_call(method_name_size: u64, payload_size: u64) -> u128 {
503    let mut dst_bytes = [0_u8; 16];
504    // SAFETY: dst_bytes is a writable sequence of 16 bytes and therefore safe to pass as ptr to ic0.cost_call
505    // The method_name_size and payload_size parameters do not affect safety
506    unsafe {
507        sys::cost_call(
508            method_name_size,
509            payload_size,
510            dst_bytes.as_mut_ptr() as usize,
511        );
512    }
513    u128::from_le_bytes(dst_bytes)
514}
515
516#[inline]
517pub fn cost_create_canister() -> u128 {
518    let mut dst_bytes = [0_u8; 16];
519    // SAFETY: dst_bytes is a writable sequence of 16 bytes and therefore safe to pass as ptr to ic0.cost_create_canister
520    unsafe {
521        sys::cost_create_canister(dst_bytes.as_mut_ptr() as usize);
522    }
523    u128::from_le_bytes(dst_bytes)
524}
525
526#[inline]
527pub fn cost_http_request(request_size: u64, max_res_bytes: u64) -> u128 {
528    let mut dst_bytes = [0_u8; 16];
529    // SAFETY: dst_bytes is a writable sequence of 16 bytes and therefore safe to pass as ptr to ic0.cost_http_request
530    // The request_size and max_res_bytes parameters do not affect safety
531    unsafe {
532        sys::cost_http_request(request_size, max_res_bytes, dst_bytes.as_mut_ptr() as usize);
533    }
534    u128::from_le_bytes(dst_bytes)
535}
536
537#[inline]
538pub fn cost_sign_with_ecdsa(key_name: &str, ecdsa_curve: u32) -> (u128, u32) {
539    let mut dst_bytes = [0_u8; 16];
540    // SAFETY:
541    // - key_name is a readable string and therefore safe to pass as ptr and len src to ic0.cost_sign_with_ecdsa
542    // - dst_bytes is a writable sequence of 16 bytes and therefore safe to pass as ptr dst to ic0.cost_sign_with_ecdsa
543    // The ecdsa_curve parameter does not affect safety
544    let code = unsafe {
545        sys::cost_sign_with_ecdsa(
546            key_name.as_ptr() as usize,
547            key_name.len(),
548            ecdsa_curve,
549            dst_bytes.as_mut_ptr() as usize,
550        )
551    };
552    (u128::from_le_bytes(dst_bytes), code)
553}
554
555#[inline]
556pub fn cost_sign_with_schnorr(key_name: &str, algorithm: u32) -> (u128, u32) {
557    let mut dst_bytes = [0_u8; 16];
558    // SAFETY:
559    // - key_name is a readable string and therefore safe to pass as ptr and len src to ic0.cost_sign_with_schnorr
560    // - dst_bytes is a writable sequence of 16 bytes and therefore safe to pass as ptr dst to ic0.cost_sign_with_schnorr
561    // The algorithm parameter does not affect safety
562    let code = unsafe {
563        sys::cost_sign_with_schnorr(
564            key_name.as_ptr() as usize,
565            key_name.len(),
566            algorithm,
567            dst_bytes.as_mut_ptr() as usize,
568        )
569    };
570    (u128::from_le_bytes(dst_bytes), code)
571}
572
573#[inline]
574pub fn cost_vetkd_derive_key(key_name: &str, vetkd_curve: u32) -> (u128, u32) {
575    let mut dst_bytes = [0_u8; 16];
576    // SAFETY:
577    // - key_name is a readable string and therefore safe to pass as ptr and len path to ic0.cost_vetkd_derive_key
578    // - dst_bytes is a writable sequence of 16 bytes and therefore safe to pass as ptr dst to ic0.cost_vetkd_derive_key
579    // The vetkd_curve parameter does not affect safety
580    let code = unsafe {
581        sys::cost_vetkd_derive_key(
582            key_name.as_ptr() as usize,
583            key_name.len(),
584            vetkd_curve,
585            dst_bytes.as_mut_ptr() as usize,
586        )
587    };
588    (u128::from_le_bytes(dst_bytes), code)
589}
590
591#[inline]
592pub fn env_var_count() -> usize {
593    // SAFETY: ic0.env_var_count is always safe to call
594    unsafe { sys::env_var_count() }
595}
596
597#[inline]
598pub fn env_var_name_size(index: usize) -> usize {
599    // SAFETY: ic0.env_var_name_size is always safe to call
600    unsafe { sys::env_var_name_size(index) }
601}
602
603#[inline]
604pub fn env_var_name_copy(index: usize, dst: &mut [u8], offset: usize) {
605    // SAFETY: dst is a writable sequence of bytes and therefore safe to pass as ptr and len to ic0.env_var_name_copy
606    unsafe { sys::env_var_name_copy(index, dst.as_mut_ptr() as usize, offset, dst.len()) }
607}
608
609/// # Safety
610///
611/// This function will fully initialize `dst` (or trap if it cannot).
612#[inline]
613pub fn env_var_name_copy_uninit(index: usize, dst: &mut [MaybeUninit<u8>], offset: usize) {
614    // SAFETY: dst is a writable sequence of bytes and therefore safe to pass as ptr and len to ic0.env_var_name_copy
615    unsafe { sys::env_var_name_copy(index, dst.as_mut_ptr() as usize, offset, dst.len()) }
616}
617
618#[inline]
619pub fn env_var_name_exists(name: &str) -> u32 {
620    // SAFETY: name is a readable string and therefore safe to pass as ptr and len to ic0.env_var_name_exists
621    unsafe { sys::env_var_name_exists(name.as_ptr() as usize, name.len()) }
622}
623
624#[inline]
625pub fn env_var_value_size(name: &str) -> usize {
626    // SAFETY: name is a readable string and therefore safe to pass as ptr and len to ic0.env_var_value_size
627    unsafe { sys::env_var_value_size(name.as_ptr() as usize, name.len()) }
628}
629
630#[inline]
631pub fn env_var_value_copy(name: &str, dst: &mut [u8], offset: usize) {
632    // SAFETY:
633    // - name is a readable string and therefore safe to pass as ptr and len to ic0.env_var_value_copy
634    // - dst is a writable sequence of bytes and therefore safe to pass as ptr and len to ic0.env_var_value_copy
635    unsafe {
636        sys::env_var_value_copy(
637            name.as_ptr() as usize,
638            name.len(),
639            dst.as_mut_ptr() as usize,
640            offset,
641            dst.len(),
642        )
643    }
644}
645
646/// # Safety
647///
648/// This function will fully initialize `dst` (or trap if it cannot).
649#[inline]
650pub fn env_var_value_copy_uninit(name: &str, dst: &mut [MaybeUninit<u8>], offset: usize) {
651    // SAFETY:
652    // - name is a readable string and therefore safe to pass as ptr and len to ic0.env_var_value_copy
653    // - dst is a writable sequence of bytes and therefore safe to pass as ptr and len to ic0.env_var_value_copy
654    unsafe {
655        sys::env_var_value_copy(
656            name.as_ptr() as usize,
657            name.len(),
658            dst.as_mut_ptr() as usize,
659            offset,
660            dst.len(),
661        )
662    }
663}
664
665#[inline]
666pub fn debug_print(message: &[u8]) {
667    // SAFETY: message is a readable sequence of bytes and therefore safe to pass as ptr and len to ic0.debug_print
668    unsafe { sys::debug_print(message.as_ptr() as usize, message.len()) }
669}
670
671#[inline]
672pub fn trap(message: &[u8]) -> ! {
673    // SAFETY: message is a readable sequence of bytes and therefore safe to pass as ptr and len to ic0.trap
674    unsafe { sys::trap(message.as_ptr() as usize, message.len()) }
675    unreachable!("trap should halt execution immediately")
676}
677
678#[inline]
679fn to_high_low(x: u128) -> (u64, u64) {
680    let high = (x >> 64) as u64;
681    let low = (x & u128::from(u64::MAX)) as u64;
682    (high, low)
683}