├── .gitmodules ├── ic0 ├── LICENSE ├── util │ └── work.rs ├── CHANGELOG.md ├── Cargo.toml ├── README.md └── ic0.txt ├── ic-cdk ├── LICENSE ├── tests │ ├── compile_fail │ │ ├── no_self.rs │ │ ├── only_function.rs │ │ ├── no_generic.rs │ │ ├── only_function.stderr │ │ ├── no_self.stderr │ │ ├── no_generic.stderr │ │ ├── lifecycle_functions_should_have_no_return.rs │ │ ├── no_guard_function_for_lifecycle.rs │ │ ├── no_guard_function_for_lifecycle.stderr │ │ └── lifecycle_functions_should_have_no_return.stderr │ ├── compile_test.rs │ ├── pass │ │ ├── method_arg_same_name.rs │ │ ├── guard.rs │ │ └── blank_methods.rs │ ├── bitcoin_candid_equality.rs │ └── bitcoin.did ├── src │ ├── api │ │ ├── stable │ │ │ └── canister.rs │ │ └── management_canister │ │ │ ├── ecdsa │ │ │ ├── mod.rs │ │ │ └── types.rs │ │ │ ├── schnorr │ │ │ ├── mod.rs │ │ │ └── types.rs │ │ │ ├── mod.rs │ │ │ ├── provisional.rs │ │ │ ├── http_request │ │ │ ├── mod.rs │ │ │ └── types.rs │ │ │ └── bitcoin │ │ │ ├── mod.rs │ │ │ └── types.rs │ ├── storage.rs │ ├── futures │ │ └── internals.rs │ └── lib.rs ├── Cargo.toml └── README.md ├── ic-cdk-bindgen ├── LICENSE ├── src │ └── templates │ │ ├── types_only.hbs │ │ ├── static_callee.hbs │ │ └── dynamic_callee.hbs ├── Cargo.toml ├── CHANGELOG.md └── README.md ├── ic-cdk-macros ├── LICENSE ├── Cargo.toml ├── CHANGELOG.md ├── README.md └── src │ └── lib.rs ├── ic-cdk-timers ├── LICENSE ├── build.rs ├── Cargo.toml ├── README.md ├── CHANGELOG.md └── src │ ├── state.rs │ └── timer_executor.rs ├── .github ├── CODEOWNERS ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── conventional-commits.yml ├── ic-management-canister-types ├── LICENSE ├── Cargo.toml ├── README.md ├── CHANGELOG.md └── tests │ └── candid_equality.rs ├── legacy ├── ic-cdk-optimizer │ ├── LICENSE │ ├── .gitignore │ ├── README.md │ ├── CHANGELOG.md │ ├── src │ │ ├── passes │ │ │ └── binaryen.rs │ │ ├── passes.rs │ │ └── main.rs │ └── Cargo.toml ├── ic-response-codes │ ├── LICENSE │ ├── README.md │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── ic-certified-assets │ └── README.md ├── library ├── ic-certified-map │ ├── LICENSE │ ├── Cargo.toml │ ├── README.md │ ├── src │ │ ├── hashtree │ │ │ └── test.rs │ │ └── lib.rs │ └── CHANGELOG.md └── ic-ledger-types │ ├── LICENSE │ ├── README.md │ ├── Cargo.toml │ └── CHANGELOG.md ├── clippy.toml ├── ic-cdk-executor ├── build.rs ├── Cargo.toml ├── CHANGELOG.md ├── README.md └── src │ └── lib.rs ├── e2e-tests ├── src │ └── bin │ │ ├── bindgen_callee │ │ ├── callee.did │ │ ├── management.toml │ │ └── main.rs │ │ ├── macros │ │ ├── canister.proto │ │ └── main.rs │ │ ├── bindgen.rs │ │ ├── simple_kv_store.rs │ │ ├── bitcoin_canister.rs │ │ ├── chunk.rs │ │ ├── canister_info.rs │ │ ├── http_request.rs │ │ └── timers.rs ├── build.rs ├── tests │ ├── call.rs │ ├── simple_kv_store.rs │ ├── management_canister.rs │ ├── http_request.rs │ ├── chunk.rs │ ├── macros.rs │ ├── bindgen.rs │ └── test_utilities.rs └── Cargo.toml ├── rust-toolchain.toml ├── .gitignore ├── .mergify.yml ├── scripts └── download_pocket_ic_server.sh ├── Cargo.toml ├── TROUBLESHOOTING.md └── README.md /.gitmodules: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ic0/LICENSE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /ic-cdk/LICENSE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /ic-cdk-bindgen/LICENSE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /ic-cdk-macros/LICENSE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /ic-cdk-timers/LICENSE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @dfinity/sdk 2 | -------------------------------------------------------------------------------- /ic-management-canister-types/LICENSE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /legacy/ic-cdk-optimizer/LICENSE: -------------------------------------------------------------------------------- 1 | ../../LICENSE -------------------------------------------------------------------------------- /legacy/ic-response-codes/LICENSE: -------------------------------------------------------------------------------- 1 | ../../LICENSE -------------------------------------------------------------------------------- /library/ic-certified-map/LICENSE: -------------------------------------------------------------------------------- 1 | ../../LICENSE -------------------------------------------------------------------------------- /library/ic-ledger-types/LICENSE: -------------------------------------------------------------------------------- 1 | ../../LICENSE -------------------------------------------------------------------------------- /legacy/ic-cdk-optimizer/.gitignore: -------------------------------------------------------------------------------- 1 | !Cargo.lock 2 | -------------------------------------------------------------------------------- /clippy.toml: -------------------------------------------------------------------------------- 1 | doc-valid-idents = ["VetKD", "PocketIC", "TxID", "UTXO", "McJones", ".."] 2 | -------------------------------------------------------------------------------- /ic-cdk-timers/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // dummy build script, required by `package.links` 3 | } 4 | -------------------------------------------------------------------------------- /ic-cdk-executor/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // dummy build script, required by `package.links` 3 | } 4 | -------------------------------------------------------------------------------- /ic-cdk/tests/compile_fail/no_self.rs: -------------------------------------------------------------------------------- 1 | use ic_cdk::query; 2 | 3 | #[query] 4 | fn method(self) {} 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /ic-cdk/tests/compile_fail/only_function.rs: -------------------------------------------------------------------------------- 1 | use ic_cdk::query; 2 | 3 | #[query] 4 | struct S; 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /ic-cdk/tests/compile_fail/no_generic.rs: -------------------------------------------------------------------------------- 1 | use ic_cdk::query; 2 | 3 | #[query] 4 | fn method(_arg: T) {} 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /library/ic-ledger-types/README.md: -------------------------------------------------------------------------------- 1 | # ICP Ledger types 2 | 3 | A library of types to communicate with the ICP ledger canister. 4 | -------------------------------------------------------------------------------- /e2e-tests/src/bin/bindgen_callee/callee.did: -------------------------------------------------------------------------------- 1 | service : { 2 | add : (int32, int32) -> (int32); 3 | echo : (text) -> (text); 4 | } 5 | -------------------------------------------------------------------------------- /ic0/util/work.rs: -------------------------------------------------------------------------------- 1 | // This file should compile on windows 2 | fn main() { 3 | unsafe { 4 | ic0::sys::trap(0, 0); 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /legacy/ic-certified-assets/README.md: -------------------------------------------------------------------------------- 1 | # Notice 2 | 3 | The `ic-certified-assets` crate has been moved to the [sdk](https://github.com/dfinity/sdk) repo. 4 | -------------------------------------------------------------------------------- /ic-cdk/tests/compile_test.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn ui() { 3 | let t = trybuild::TestCases::new(); 4 | t.pass("tests/pass/*.rs"); 5 | t.compile_fail("tests/compile_fail/*.rs"); 6 | } 7 | -------------------------------------------------------------------------------- /ic-cdk/tests/compile_fail/only_function.stderr: -------------------------------------------------------------------------------- 1 | error: #[query] must be above a function. 2 | expected `fn` 3 | --> tests/compile_fail/only_function.rs:4:1 4 | | 5 | 4 | struct S; 6 | | ^^^^^^ 7 | -------------------------------------------------------------------------------- /ic-cdk-bindgen/src/templates/types_only.hbs: -------------------------------------------------------------------------------- 1 | use {{candid_crate}}::{CandidType, Deserialize, Principal}; 2 | use ic_cdk::call::{Call, CallResult}; 3 | // START -- Types 4 | {{type_defs}} 5 | // END -- Types 6 | -------------------------------------------------------------------------------- /ic-cdk/tests/compile_fail/no_self.stderr: -------------------------------------------------------------------------------- 1 | error: #[query] cannot be above functions with `self` as a parameter. 2 | --> tests/compile_fail/no_self.rs:4:11 3 | | 4 | 4 | fn method(self) {} 5 | | ^^^^ 6 | -------------------------------------------------------------------------------- /ic-cdk/tests/compile_fail/no_generic.stderr: -------------------------------------------------------------------------------- 1 | error: #[query] must be above a function with no generic parameters. 2 | --> tests/compile_fail/no_generic.rs:4:10 3 | | 4 | 4 | fn method(_arg: T) {} 5 | | ^ 6 | -------------------------------------------------------------------------------- /ic-cdk/tests/pass/method_arg_same_name.rs: -------------------------------------------------------------------------------- 1 | use ic_cdk::{query, update}; 2 | 3 | #[update] 4 | fn foo(foo: i32) -> i32 { 5 | foo 6 | } 7 | 8 | #[query] 9 | fn bar(bar: i32) -> i32 { 10 | bar 11 | } 12 | 13 | fn main() {} 14 | -------------------------------------------------------------------------------- /e2e-tests/src/bin/bindgen_callee/management.toml: -------------------------------------------------------------------------------- 1 | [rust] 2 | visibility = "pub(crate)" 3 | attributes = "#[derive(CandidType, Deserialize, Clone, Debug)]" 4 | blob.use_type = "Vec" 5 | change_details.variant.creation.name = "CreationRecord" 6 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | # This is not the MSRV, we just want to use the latest stable Rust for development 3 | # MSRV is defined in the `Cargo.toml` file 4 | channel = "stable" 5 | targets = ["wasm32-unknown-unknown"] 6 | components = ["rustfmt", "clippy"] 7 | -------------------------------------------------------------------------------- /legacy/ic-response-codes/README.md: -------------------------------------------------------------------------------- 1 | > [!NOTE] 2 | > 3 | > This library is discontinued and will not be updated. 4 | > 5 | > Please use [`ic-error-types`](https://crates.io/crates/ic-error-types) instead. 6 | 7 | # ic-response-codes 8 | 9 | The codes in the response of Internet Computer API request or inter-canister call. 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IDEs 2 | .idea/ 3 | .vscode/ 4 | 5 | # Build Output 6 | target/ 7 | 8 | # DFX 9 | .dfx/ 10 | 11 | # The root Cargo.lock is ignored because the workspace only has library crates. 12 | # The candid-extractor has its own Cargo.lock file which is tracked to build the binary. 13 | Cargo.lock 14 | 15 | # Generated bindings 16 | **/declarations/ 17 | -------------------------------------------------------------------------------- /legacy/ic-cdk-optimizer/README.md: -------------------------------------------------------------------------------- 1 | # Deprecated 2 | This tool is deprecated since Sep 2022. The last release of it was [v0.3.5](https://github.com/dfinity/cdk-rs/releases/tag/0.3.5). 3 | 4 | 5 | Use ic-wasm shrink instead to reduce size of WASM modules for Internet Computer. Check [ic-wasm](https://github.com/dfinity/ic-wasm) repo here. 6 | 7 | ## ic-cdk-optimizer 8 | Optimizer to reduce the size of CDK WASMs. 9 | -------------------------------------------------------------------------------- /ic-cdk/tests/pass/guard.rs: -------------------------------------------------------------------------------- 1 | use ic_cdk::{query, update}; 2 | 3 | fn guard1() -> Result<(), String> { 4 | Ok(()) 5 | } 6 | 7 | fn guard2() -> Result<(), String> { 8 | Ok(()) 9 | } 10 | 11 | #[update(guard = "guard1")] 12 | fn update_1_guard() {} 13 | 14 | #[update(guard = "guard1", guard = "guard2")] 15 | fn update_2_guards() {} 16 | 17 | #[query(guard = "guard1")] 18 | fn query_1_guard() {} 19 | 20 | #[query(guard = "guard1", guard = "guard2")] 21 | fn query_2_guards() {} 22 | 23 | fn main() {} 24 | -------------------------------------------------------------------------------- /ic-cdk/tests/compile_fail/lifecycle_functions_should_have_no_return.rs: -------------------------------------------------------------------------------- 1 | use ic_cdk::{heartbeat, init, inspect_message, on_low_wasm_memory, post_upgrade, pre_upgrade}; 2 | 3 | #[init] 4 | fn init() -> u32 {} 5 | 6 | #[pre_upgrade] 7 | fn pre_upgrade() -> u32 {} 8 | 9 | #[post_upgrade] 10 | fn post_upgrade() -> u32 {} 11 | 12 | #[heartbeat] 13 | fn heartbeat() -> u32 {} 14 | 15 | #[inspect_message] 16 | fn inspect_message() -> u32 {} 17 | 18 | #[on_low_wasm_memory] 19 | fn on_low_wasm_memory() -> u32 {} 20 | 21 | fn main() {} 22 | -------------------------------------------------------------------------------- /e2e-tests/src/bin/macros/canister.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package canister; 4 | 5 | service ExampleService { 6 | rpc MethodOne (MethodOneRequest) returns (MethodOneResponse); 7 | rpc MethodTwo (MethodTwoRequest) returns (MethodTwoResponse); 8 | } 9 | 10 | message MethodOneRequest { 11 | string input = 1; 12 | } 13 | 14 | message MethodOneResponse { 15 | int32 result = 1; 16 | } 17 | 18 | message MethodTwoRequest { 19 | repeated float values = 1; 20 | } 21 | 22 | message MethodTwoResponse { 23 | bool success = 1; 24 | string message = 2; 25 | } 26 | -------------------------------------------------------------------------------- /ic-cdk-bindgen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ic-cdk-bindgen" 3 | version = "0.2.0-alpha.2" 4 | authors.workspace = true 5 | edition.workspace = true 6 | license.workspace = true 7 | rust-version.workspace = true 8 | repository.workspace = true 9 | description = "Internet Computer Binding Generator." 10 | readme = "README.md" 11 | categories = ["api-bindings", "development-tools::ffi"] 12 | keywords = ["internet-computer", "types", "dfinity", "canister", "cdk"] 13 | include = ["src", "Cargo.toml", "LICENSE", "README.md"] 14 | 15 | [dependencies] 16 | candid.workspace = true 17 | candid_parser.workspace = true 18 | thiserror.workspace = true 19 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. 4 | 5 | Fixes # (issue) 6 | 7 | # How Has This Been Tested? 8 | 9 | Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration. 10 | 11 | # Checklist: 12 | 13 | - [ ] The title of this PR complies with [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/). 14 | - [ ] I have edited the CHANGELOG accordingly. 15 | - [ ] I have made corresponding changes to the documentation. 16 | -------------------------------------------------------------------------------- /legacy/ic-response-codes/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | [package] 4 | name = "ic-response-codes" 5 | version = "0.1.2" 6 | authors = ["DFINITY Stiftung "] 7 | edition = "2021" 8 | repository = "https://github.com/dfinity/cdk-rs" 9 | rust-version = "1.75.0" 10 | license = "Apache-2.0" 11 | description = "Internet Computer Response Codes" 12 | homepage = "https://docs.rs/ic-response-codes" 13 | documentation = "https://docs.rs/ic-response-codes" 14 | readme = "README.md" 15 | categories = ["api-bindings", "data-structures"] 16 | keywords = ["internet-computer", "types", "dfinity"] 17 | include = ["src", "Cargo.toml", "LICENSE", "README.md"] 18 | 19 | [dependencies] 20 | -------------------------------------------------------------------------------- /ic-cdk/tests/compile_fail/no_guard_function_for_lifecycle.rs: -------------------------------------------------------------------------------- 1 | use ic_cdk::{heartbeat, init, inspect_message, on_low_wasm_memory, post_upgrade, pre_upgrade}; 2 | 3 | fn guard_function() -> Result<(), String> { 4 | unimplemented!() 5 | } 6 | 7 | #[init(guard = "guard_function")] 8 | fn init() {} 9 | 10 | #[pre_upgrade(guard = "guard_function")] 11 | fn pre_upgrade() {} 12 | 13 | #[post_upgrade(guard = "guard_function")] 14 | fn post_upgrade() {} 15 | 16 | #[heartbeat(guard = "guard_function")] 17 | fn heartbeat() {} 18 | 19 | #[inspect_message(guard = "guard_function")] 20 | fn inspect_message() {} 21 | 22 | #[on_low_wasm_memory(guard = "guard_function")] 23 | fn on_low_wasm_memory() {} 24 | 25 | fn main() {} 26 | -------------------------------------------------------------------------------- /legacy/ic-cdk-optimizer/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [0.3.5] - 2022-09-15 8 | This is the final release of this deprecated tool. 9 | 10 | ### Added 11 | - Specifying `-` as the input or output argument refers to stdin or stdout respectively (#230) 12 | 13 | ### Changed 14 | - Update clap to 3.1 (#209) 15 | - The output argument defaults to the input argument if unspecified (#230) 16 | 17 | ## [0.3.4] - 2022-02-07 18 | ### Fixed 19 | - Actually print version with --version (#196) 20 | -------------------------------------------------------------------------------- /ic0/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to the project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [unreleased] 9 | 10 | ## [1.0.1] - 2025-09-11 11 | 12 | ### Added 13 | 14 | - Added `env_var_*` API. 15 | 16 | ## [1.0.0] - 2025-06-25 17 | 18 | ### Changed 19 | 20 | - Introduced new safe API in the crate root. The raw C bindings have been moved to `ic0::sys`. 21 | 22 | ### Migration guide 23 | 24 | - Replace `ic0::*` with `ic0::sys::*`, e.g., `ic0::msg_arg_data_size()` -> `ic0::sys::msg_arg_data_size()`. 25 | 26 | ## [0.25.1] - 2025-06-04 27 | 28 | Changelog introduced 29 | -------------------------------------------------------------------------------- /.mergify.yml: -------------------------------------------------------------------------------- 1 | pull_request_rules: 2 | - name: Automatic merge 3 | conditions: 4 | - "#approved-reviews-by>=1" 5 | - "#changes-requested-reviews-by=0" 6 | - status-success=conventional-pr-title 7 | - label=automerge-squash 8 | actions: 9 | merge: 10 | method: squash 11 | strict: smart 12 | commit_message: title+body 13 | delete_head_branch: {} 14 | 15 | - name: Clean up automerge tags 16 | conditions: 17 | - closed 18 | actions: 19 | label: 20 | remove: 21 | - automerge-squash 22 | 23 | - name: Auto-approve auto-PRs 24 | conditions: 25 | - author=dfinity-bot 26 | actions: 27 | review: 28 | type: APPROVE 29 | message: This bot trusts that bot 30 | -------------------------------------------------------------------------------- /ic-management-canister-types/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ic-management-canister-types" 3 | version = "0.5.0" 4 | authors.workspace = true 5 | edition.workspace = true 6 | repository.workspace = true 7 | rust-version.workspace = true 8 | license.workspace = true 9 | description = "Types for calling the IC management canister" 10 | homepage = "https://docs.rs/ic-management-canister-types" 11 | documentation = "https://docs.rs/ic-management-canister-types" 12 | readme = "README.md" 13 | categories = ["api-bindings", "data-structures"] 14 | keywords = ["internet-computer", "types", "dfinity", "canister"] 15 | include = ["src", "Cargo.toml", "LICENSE", "README.md"] 16 | 17 | [dependencies] 18 | candid.workspace = true 19 | serde.workspace = true 20 | serde_bytes.workspace = true 21 | 22 | [dev-dependencies] 23 | candid_parser.workspace = true 24 | -------------------------------------------------------------------------------- /ic0/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ic0" 3 | version = "1.0.1" 4 | authors.workspace = true 5 | edition.workspace = true 6 | license.workspace = true 7 | rust-version.workspace = true 8 | repository.workspace = true 9 | description = "Internet Computer System API Binding." 10 | readme = "README.md" 11 | categories = ["api-bindings", "development-tools::ffi"] 12 | keywords = ["internet-computer", "types", "dfinity", "canister", "cdk"] 13 | include = ["src", "Cargo.toml", "LICENSE", "README.md"] 14 | 15 | [dev-dependencies] 16 | quote.workspace = true 17 | syn = { workspace = true, features = ["parsing", "full", "extra-traits"] } 18 | 19 | # This is not a real example but a utility for auto-generating ic0.rs 20 | [[example]] 21 | name = "ic0build" 22 | path = "util/ic0build.rs" 23 | 24 | [[example]] 25 | name = "work" 26 | path = "util/work.rs" 27 | -------------------------------------------------------------------------------- /e2e-tests/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // For the `macros` test 3 | prost_build::compile_protos(&["src/bin/macros/canister.proto"], &["src/"]) 4 | .expect("Failed to compile protos"); 5 | 6 | // For the `bindgen` test 7 | // Static Callee mode bindgen for the management_canister 8 | ic_cdk_bindgen::Config::new( 9 | "management_canister", 10 | "../ic-management-canister-types/tests/ic.did", 11 | ) 12 | .set_type_selector_config("src/bin/bindgen_callee/management.toml") 13 | .static_callee(candid::Principal::management_canister()) 14 | .generate(); 15 | // Dynamic Callee mode bindgen for the bindgen_callee 16 | ic_cdk_bindgen::Config::new("bindgen_callee", "src/bin/bindgen_callee/callee.did") 17 | .dynamic_callee("ICP_CANISTER_ID:bindgen_callee") 18 | .generate(); 19 | } 20 | -------------------------------------------------------------------------------- /e2e-tests/src/bin/bindgen_callee/main.rs: -------------------------------------------------------------------------------- 1 | use ic_cdk::{export_candid, update}; 2 | 3 | #[update] 4 | async fn add(a: i32, b: i32) -> i32 { 5 | a + b 6 | } 7 | 8 | #[update] 9 | async fn echo(s: String) -> String { 10 | s 11 | } 12 | 13 | export_candid! {} 14 | 15 | fn main() {} 16 | 17 | #[cfg(test)] 18 | mod tests { 19 | use candid_parser::utils::{service_equal, CandidSource}; 20 | 21 | #[test] 22 | fn candid_equality_test() { 23 | let expected = include_str!("callee.did"); 24 | let expected_candid = CandidSource::Text(expected); 25 | 26 | let actual = super::__export_service(); 27 | let actual_candid = CandidSource::Text(&actual); 28 | 29 | let result = service_equal(expected_candid, actual_candid); 30 | assert!(result.is_ok(), "{:?}", result.unwrap_err()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /legacy/ic-cdk-optimizer/src/passes/binaryen.rs: -------------------------------------------------------------------------------- 1 | use crate::passes::{OptimizationPass, PassResult}; 2 | use binaryen::{CodegenConfig, Module}; 3 | 4 | pub struct BinaryenPass; 5 | 6 | impl OptimizationPass for BinaryenPass { 7 | fn short_name(&self) -> String { 8 | String::from("binaryen") 9 | } 10 | 11 | fn description(&self) -> String { 12 | String::from("Execute a binaryen optimization pass on your WASM.") 13 | } 14 | 15 | fn opt(&self, wasm: &[u8]) -> PassResult { 16 | let mut module = 17 | Module::read(wasm).map_err(|_| String::from("Could not load module..."))?; 18 | 19 | module.optimize(&CodegenConfig { 20 | debug_info: false, 21 | optimization_level: 2, 22 | shrink_level: 2, 23 | }); 24 | 25 | Ok(module.write()) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ic-cdk-executor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ic-cdk-executor" 3 | version = "2.0.0" 4 | authors.workspace = true 5 | edition.workspace = true 6 | repository.workspace = true 7 | rust-version.workspace = true 8 | license.workspace = true 9 | description = "Async executor for `ic-cdk`" 10 | categories = ["asynchronous", "rust-patterns"] 11 | keywords = ["internet-computer", "dfinity", "canister", "cdk"] 12 | # DO NOT change this field, not even to update the link, unless you are updating every existing version of the package. 13 | # Update the link by updating the links-pin tag. 14 | links = "ic-cdk async executor, see https://github.com/dfinity/cdk-rs/blob/links-pin/TROUBLESHOOTING.md" 15 | 16 | [dependencies] 17 | ic0.workspace = true 18 | slotmap.workspace = true 19 | smallvec = "1.15.1" 20 | 21 | [package.metadata.docs.rs] 22 | targets = ["wasm32-unknown-unknown"] 23 | -------------------------------------------------------------------------------- /ic0/README.md: -------------------------------------------------------------------------------- 1 | # ic0 2 | 3 | Internet Computer System API binding. 4 | 5 | ## What 6 | 7 | `ic0` is simply a safe Rust translation of the System API as described in the [IC interface specification][1]. The unsafe direct imports can be found in the `ic0::sys` module. 8 | 9 | ## Update 10 | 11 | `ic0` keeps in step with the IC interface specification. Particularly, `ic0` is directly generated from the [system API][1] in that repo. 12 | 13 | When interface-spec releases a new version that modify [system API][1]: 14 | 15 | 1. replace `ic0.txt` in the root of this project; 16 | 2. copy any new function headers to `manual_safety_comments.txt`, and add a safety comment for the function; 17 | 3. execute `cargo run --example=ic0build`; 18 | 19 | `src/sys.rs` should be updated. 20 | 21 | [1]: https://internetcomputer.org/docs/current/references/ic-interface-spec/#system-api-imports 22 | -------------------------------------------------------------------------------- /scripts/download_pocket_ic_server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | uname_sys=$(uname -s | tr '[:upper:]' '[:lower:]') 6 | echo "uname_sys: $uname_sys" 7 | 8 | SCRIPTS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 9 | cd "$SCRIPTS_DIR/../e2e-tests" 10 | # extract the tag from e2e-tests/Cargo.toml 11 | tag=$(grep -E 'pocket-ic.*tag' Cargo.toml | sed -n "s/.*tag *= *\"\([^\"]*\)\".*/\1/p") 12 | 13 | ARTIFACTS_DIR="$SCRIPTS_DIR/../target/e2e-tests-artifacts" 14 | mkdir -p "$ARTIFACTS_DIR" 15 | cd "$ARTIFACTS_DIR" 16 | echo -n "$tag" > pocket-ic-tag 17 | curl -sL "https://github.com/dfinity/ic/releases/download/$tag/pocket-ic-x86_64-$uname_sys.gz" --output pocket-ic.gz 18 | gzip -df pocket-ic.gz 19 | chmod a+x pocket-ic 20 | ./pocket-ic --version 21 | 22 | if [[ "$uname_sys" == "darwin" ]]; then 23 | xattr -dr com.apple.quarantine pocket-ic 24 | fi 25 | -------------------------------------------------------------------------------- /ic-cdk/src/api/stable/canister.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | /// A standard implementation of [`StableMemory`]. 4 | /// 5 | /// Useful for creating [`StableWriter`] and [`StableReader`]. 6 | #[derive(Default, Debug, Copy, Clone)] 7 | pub struct CanisterStableMemory {} 8 | 9 | impl StableMemory for CanisterStableMemory { 10 | fn stable_size(&self) -> u64 { 11 | ic0::stable64_size() 12 | } 13 | 14 | fn stable_grow(&self, new_pages: u64) -> Result { 15 | match ic0::stable64_grow(new_pages) { 16 | u64::MAX => Err(StableMemoryError::OutOfMemory), 17 | x => Ok(x), 18 | } 19 | } 20 | 21 | fn stable_write(&self, offset: u64, buf: &[u8]) { 22 | ic0::stable64_write(buf, offset); 23 | } 24 | 25 | fn stable_read(&self, offset: u64, buf: &mut [u8]) { 26 | ic0::stable64_read(buf, offset); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /e2e-tests/src/bin/bindgen.rs: -------------------------------------------------------------------------------- 1 | use ic_cdk::update; 2 | 3 | // It's a common practice to suppress the warnings from generated code. 4 | // These attributes are scoped to the module to avoid affecting other code. 5 | #[allow(dead_code, unused_imports)] 6 | mod management_canister { 7 | include!(concat!(env!("OUT_DIR"), "/management_canister.rs")); 8 | } 9 | 10 | #[update] 11 | async fn call_management_canister() { 12 | // In modern IDE/editors like VSCode, you can often use "Go to Definition" or similar features 13 | // to quickly navigate to the generated bindings. 14 | let _rand = management_canister::raw_rand().await.unwrap(); 15 | } 16 | 17 | #[allow(dead_code, unused_imports)] 18 | mod bindgen_callee { 19 | include!(concat!(env!("OUT_DIR"), "/bindgen_callee.rs")); 20 | } 21 | 22 | #[update] 23 | async fn call_bindgen_callee() { 24 | assert_eq!(bindgen_callee::add(&1, &2).await.unwrap(), 3); 25 | } 26 | 27 | fn main() {} 28 | -------------------------------------------------------------------------------- /e2e-tests/tests/call.rs: -------------------------------------------------------------------------------- 1 | mod test_utilities; 2 | use test_utilities::{cargo_build_canister, pic_base, update}; 3 | 4 | #[test] 5 | fn call() { 6 | let wasm = cargo_build_canister("call"); 7 | let pic = pic_base().build(); 8 | let canister_id = pic.create_canister(); 9 | pic.add_cycles(canister_id, 100_000_000_000_000); 10 | pic.install_canister(canister_id, wasm, vec![], None); 11 | let _: () = update(&pic, canister_id, "call_foo", ()).unwrap(); 12 | let _: () = update(&pic, canister_id, "call_echo", ()).unwrap(); 13 | let _: () = update(&pic, canister_id, "retry_calls", ()).unwrap(); 14 | let _: () = update(&pic, canister_id, "join_calls", ()).unwrap(); 15 | let _: () = update( 16 | &pic, 17 | canister_id, 18 | "insufficient_liquid_cycle_balance_error", 19 | (), 20 | ) 21 | .unwrap(); 22 | 23 | let _: () = update(&pic, canister_id, "call_error_ext", ()).unwrap(); 24 | } 25 | -------------------------------------------------------------------------------- /ic-cdk-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ic-cdk-macros" 3 | version = "0.19.0" # sync with ic-cdk 4 | authors.workspace = true 5 | edition.workspace = true 6 | license.workspace = true 7 | rust-version.workspace = true 8 | repository.workspace = true 9 | description = "Canister Developer Kit macros." 10 | homepage = "https://docs.rs/ic-cdk-macros" 11 | documentation = "https://docs.rs/ic-cdk-macros" 12 | readme = "README.md" 13 | categories = ["api-bindings", "data-structures", "no-std", "development-tools::ffi"] 14 | keywords = ["internet-computer", "types", "dfinity", "canister", "cdk"] 15 | include = ["src", "Cargo.toml", "LICENSE", "README.md"] 16 | 17 | [lib] 18 | proc-macro = true 19 | 20 | [dependencies] 21 | candid.workspace = true 22 | darling = "0.20.11" 23 | proc-macro2 = "1.0" 24 | quote.workspace = true 25 | syn = { workspace = true, features = ["fold", "full", "extra-traits"] } 26 | -------------------------------------------------------------------------------- /library/ic-certified-map/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ic-certified-map" 3 | version = "0.4.1" 4 | authors.workspace = true 5 | edition.workspace = true 6 | license.workspace = true 7 | rust-version.workspace = true 8 | repository.workspace = true 9 | description = "Merkleized map data structure." 10 | homepage = "https://docs.rs/ic-certified-map" 11 | documentation = "https://docs.rs/ic-certified-map" 12 | readme = "README.md" 13 | categories = [ 14 | "data-structures", 15 | "cryptography", 16 | "cryptography::cryptocurrencies", 17 | ] 18 | keywords = ["internet-computer", "types", "dfinity", "map"] 19 | include = ["src", "Cargo.toml", "CHANGELOG.md", "LICENSE", "README.md"] 20 | 21 | [dependencies] 22 | serde.workspace = true 23 | serde_bytes.workspace = true 24 | sha2.workspace = true 25 | candid.workspace = true 26 | 27 | [dev-dependencies] 28 | hex.workspace = true 29 | serde_cbor = "0.11" 30 | ic-cdk.workspace = true 31 | bincode = "1.3.3" 32 | -------------------------------------------------------------------------------- /e2e-tests/src/bin/simple_kv_store.rs: -------------------------------------------------------------------------------- 1 | use ic_cdk::{post_upgrade, pre_upgrade, query, update}; 2 | use serde_bytes::ByteBuf; 3 | use std::cell::RefCell; 4 | use std::collections::BTreeMap; 5 | 6 | type Store = BTreeMap; 7 | 8 | thread_local! { 9 | static STORE: RefCell = RefCell::default(); 10 | } 11 | 12 | #[update] 13 | fn insert(key: String, value: ByteBuf) { 14 | STORE.with(|store| store.borrow_mut().insert(key, value)); 15 | } 16 | 17 | #[query] 18 | fn lookup(key: String) -> Option { 19 | STORE.with(|store| store.borrow().get(&key).cloned()) 20 | } 21 | 22 | #[pre_upgrade] 23 | fn pre_upgrade() { 24 | STORE.with(|store| ic_cdk::storage::stable_save((store,)).unwrap()); 25 | } 26 | 27 | #[post_upgrade] 28 | fn post_upgrade() { 29 | let (persisted_store,): (Store,) = ic_cdk::storage::stable_restore().unwrap(); 30 | STORE.with(|store| *store.borrow_mut() = persisted_store); 31 | } 32 | 33 | fn main() {} 34 | -------------------------------------------------------------------------------- /library/ic-ledger-types/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ic-ledger-types" 3 | version = "0.16.0" 4 | authors.workspace = true 5 | edition.workspace = true 6 | license.workspace = true 7 | rust-version.workspace = true 8 | repository.workspace = true 9 | description = "Types for interacting with the ICP ledger canister." 10 | homepage = "https://docs.rs/ic-ledger-types" 11 | documentation = "https://docs.rs/ic-ledger-types" 12 | readme = "README.md" 13 | keywords = ["internet-computer", "ledger"] 14 | categories = ["cryptography::cryptocurrencies", "data-structures"] 15 | include = ["src", "Cargo.toml", "CHANGELOG.md", "LICENSE", "README.md"] 16 | 17 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 18 | 19 | [dependencies] 20 | ic-cdk.workspace = true 21 | candid.workspace = true 22 | crc32fast = "1.2.0" 23 | hex.workspace = true 24 | serde.workspace = true 25 | serde_bytes.workspace = true 26 | sha2.workspace = true 27 | -------------------------------------------------------------------------------- /ic-cdk/src/storage.rs: -------------------------------------------------------------------------------- 1 | //! Tools for managing stable storage of data in a canister. 2 | use crate::stable; 3 | 4 | /// Saves the storage into the stable memory. 5 | /// 6 | /// This will override any value previously stored in stable memory. 7 | pub fn stable_save(t: T) -> Result<(), candid::Error> 8 | where 9 | T: candid::utils::ArgumentEncoder, 10 | { 11 | candid::write_args(&mut stable::StableWriter::default(), t) 12 | } 13 | 14 | /// Restores a value from the stable memory to the storage. 15 | /// 16 | /// There can only be one value in stable memory, currently. 17 | pub fn stable_restore() -> Result 18 | where 19 | T: for<'de> candid::utils::ArgumentDecoder<'de>, 20 | { 21 | let bytes = stable::stable_bytes(); 22 | 23 | let mut de = candid::de::IDLDeserialize::new(bytes.as_slice()).map_err(|e| format!("{e:?}"))?; 24 | let res = candid::utils::ArgumentDecoder::decode(&mut de).map_err(|e| format!("{e:?}"))?; 25 | Ok(res) 26 | } 27 | -------------------------------------------------------------------------------- /legacy/ic-cdk-optimizer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | [package] 4 | name = "ic-cdk-optimizer" 5 | version = "0.3.5" 6 | authors = ["DFINITY Stiftung "] 7 | edition = "2021" 8 | description = "WASM Optimizer for the IC CDK (experimental)." 9 | homepage = "https://docs.rs/ic-cdk-optimizer" 10 | documentation = "https://docs.rs/ic-cdk-optimizer" 11 | license = "Apache-2.0" 12 | readme = "README.md" 13 | categories = ["api-bindings", "data-structures", "no-std", "command-line-utilities", "wasm"] 14 | keywords = ["internet-computer", "dfinity", "canister", "cli", "optimizer"] 15 | include = ["src", "Cargo.toml", "LICENSE", "README.md"] 16 | repository = "https://github.com/dfinity/cdk-rs" 17 | rust-version = "1.60.0" 18 | 19 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 20 | 21 | [dependencies] 22 | binaryen = "0.12.0" 23 | clap = { version = "3.1.0", features = ["derive"] } 24 | humansize = "1.1.0" 25 | wabt = "0.10.0" 26 | -------------------------------------------------------------------------------- /legacy/ic-cdk-optimizer/src/passes.rs: -------------------------------------------------------------------------------- 1 | use wabt::{wasm2wat, wat2wasm}; 2 | 3 | mod binaryen; 4 | 5 | pub type PassResult = Result, Box>; 6 | 7 | pub trait OptimizationPass { 8 | fn short_name(&self) -> String; 9 | fn description(&self) -> String; 10 | fn opt(&self, wasm: &[u8]) -> PassResult; 11 | } 12 | 13 | struct RemoveDebugSymbolsPass; 14 | 15 | impl OptimizationPass for RemoveDebugSymbolsPass { 16 | fn short_name(&self) -> String { 17 | String::from("strip_data") 18 | } 19 | 20 | fn description(&self) -> String { 21 | String::from("Stripping Unused Data Segments") 22 | } 23 | 24 | fn opt(&self, wasm: &[u8]) -> PassResult { 25 | let wat = wasm2wat(&wasm)?; 26 | Ok(wat2wasm(&wat)?) 27 | } 28 | } 29 | 30 | pub fn create() -> Vec> { 31 | vec![ 32 | Box::new(RemoveDebugSymbolsPass), 33 | Box::new(binaryen::BinaryenPass), 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /library/ic-certified-map/README.md: -------------------------------------------------------------------------------- 1 | # Certified Map 2 | 3 | This package provides a map that can be used by Internet Computer canisters to implement _certified queries_. 4 | 5 | ## Features 6 | 7 | * Incremental certification. 8 | The canister can store thousands of entries while keeping the cost of certification relatively low. 9 | 10 | * Proofs of absence. 11 | If the requested key is not present in the map, the returned tree structure allows the caller to verify that fact. 12 | 13 | * Relatively small merkle proofs. 14 | The size overhead of the certificate is O(log N), where N is the number of entries in the map. 15 | 16 | ## Implementation Details 17 | 18 | The canister uses an augmented Red-Black binary search tree to store the entries. 19 | Each node of the search tree is annotated with the root hash of the hash tree built from the subtree rooted at this node. 20 | Each time the tree is rotated or modified, the corresponding hashes are recomputed in O(1) time. 21 | -------------------------------------------------------------------------------- /ic-cdk-bindgen/src/templates/static_callee.hbs: -------------------------------------------------------------------------------- 1 | use {{candid_crate}}::{CandidType, Deserialize, Principal}; 2 | use ic_cdk::call::{Call, CallResult}; 3 | // START -- Types 4 | {{type_defs}} 5 | // END -- Types 6 | /// Canister ID: `{{canister_id}}` 7 | pub const CANISTER_ID : Principal = Principal::from_slice(&[{{principal_slice canister_id}}]); 8 | // START -- Methods 9 | {{#if methods}} 10 | {{#each methods}} 11 | {{#each this.docs}} 12 | {{../../doc_comment_prefix}}{{this}} 13 | {{/each}} 14 | pub async fn {{this.name}}({{#each this.args}}{{this.0}}: &{{this.1}}, {{/each}}) -> CallResult<{{#if (eq (len this.rets) 1)}}{{this.rets.0}}{{else}}({{#each this.rets}}{{this}}{{#unless @last}}, {{/unless}}{{/each}}){{/if}}> { 15 | Ok(Call::bounded_wait(CANISTER_ID, "{{escape_debug this.original_name}}"){{#if (eq (len this.args) 0)}}{{else}}{{#if (eq (len this.args) 1)}}.with_arg({{this.args.0.0}}){{else}}.with_args(&({{#each this.args}}{{this.0}}{{#unless @last}}, {{/unless}}{{/each}})){{/if}}{{/if}}.await?.candid()?) 16 | } 17 | {{/each}} 18 | {{/if}} 19 | // END -- Methods 20 | -------------------------------------------------------------------------------- /e2e-tests/tests/simple_kv_store.rs: -------------------------------------------------------------------------------- 1 | use pocket_ic::query_candid; 2 | use serde_bytes::ByteBuf; 3 | 4 | mod test_utilities; 5 | use test_utilities::{cargo_build_canister, pic_base, update}; 6 | 7 | /// Checks that a canister that uses [`ic_cdk::storage::stable_save`] 8 | /// and [`ic_cdk::storage::stable_restore`] functions can keep its data 9 | /// across upgrades. 10 | #[test] 11 | fn test_storage_roundtrip() { 12 | let wasm = cargo_build_canister("simple_kv_store"); 13 | let pic = pic_base().build(); 14 | let canister_id = pic.create_canister(); 15 | pic.add_cycles(canister_id, 2_000_000_000_000); 16 | pic.install_canister(canister_id, wasm.clone(), vec![], None); 17 | 18 | let () = update(&pic, canister_id, "insert", (&"candid", &b"did")) 19 | .expect("failed to insert 'candid'"); 20 | 21 | pic.upgrade_canister(canister_id, wasm, vec![], None) 22 | .expect("failed to upgrade the simple_kv_store canister"); 23 | 24 | let (result,): (Option,) = 25 | query_candid(&pic, canister_id, "lookup", (&"candid",)).expect("failed to lookup 'candid'"); 26 | assert_eq!(result, Some(ByteBuf::from(b"did".to_vec()))); 27 | } 28 | -------------------------------------------------------------------------------- /e2e-tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ic-cdk-e2e-tests" 3 | version = "0.1.0" 4 | authors = ["DFINITY Stiftung "] 5 | edition = "2021" 6 | description = "End-to-end tests for the Rust Canister Development Kit" 7 | license = "Apache-2.0" 8 | repository = "https://github.com/dfinity/cdk-rs" 9 | publish = false 10 | 11 | [dependencies] 12 | async-channel = "2.3.1" 13 | candid.workspace = true 14 | env_logger = "0.11.5" 15 | escargot = { version = "0.5.7" } 16 | futures.workspace = true 17 | ic-cdk = { workspace = true, features = ["transform-closure"] } 18 | ic-cdk-timers.workspace = true 19 | lazy_static = "1.4.0" 20 | serde.workspace = true 21 | serde_bytes.workspace = true 22 | sha2.workspace = true 23 | prost = "0.14" 24 | 25 | [dev-dependencies] 26 | candid_parser.workspace = true 27 | cargo_metadata = "0.21" 28 | futures = "0.3" 29 | hex.workspace = true 30 | ic-vetkd-utils = { git = "https://github.com/dfinity/ic", rev = "95231520" } 31 | pocket-ic = { git = "https://github.com/dfinity/ic", tag = "release-2025-10-17_03-17-base" } 32 | reqwest = "0.12" 33 | 34 | [build-dependencies] 35 | prost-build = "0.14" 36 | ic-cdk-bindgen.workspace = true 37 | candid.workspace = true 38 | -------------------------------------------------------------------------------- /ic-cdk/src/futures/internals.rs: -------------------------------------------------------------------------------- 1 | //! Internals used by the attribute macros. 2 | //! 3 | //! You do not need to use this module unless you are deliberately avoiding the attribute macros. 4 | 5 | /// Execute an update function in a context that allows calling [`spawn`](super::spawn). 6 | /// 7 | /// You do not need to worry about this function unless you are avoiding the attribute macros. 8 | /// 9 | /// Background tasks will be polled in the process (and will not be run otherwise). 10 | /// Panics if called inside an existing executor context. 11 | pub fn in_executor_context(f: impl FnOnce() -> R) -> R { 12 | ic_cdk_executor::in_tracking_executor_context(f) 13 | } 14 | 15 | /// Execute a composite query function in a context that allows calling [`spawn`](super::spawn). 16 | /// 17 | /// You do not need to worry about this function unless you are avoiding the attribute macros. 18 | /// 19 | /// Background composite query tasks will be polled in the process (and will not be run otherwise). 20 | /// Panics if called inside an existing executor context. 21 | pub fn in_query_executor_context(f: impl FnOnce() -> R) -> R { 22 | ic_cdk_executor::in_tracking_query_executor_context(f) 23 | } 24 | -------------------------------------------------------------------------------- /ic-cdk-timers/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ic-cdk-timers" 3 | version = "1.0.0" 4 | authors.workspace = true 5 | edition.workspace = true 6 | license.workspace = true 7 | rust-version.workspace = true 8 | repository.workspace = true 9 | description = "Timers library for the Rust CDK." 10 | homepage = "https://docs.rs/ic-cdk" 11 | documentation = "https://docs.rs/ic-cdk-timers" 12 | readme = "README.md" 13 | categories = ["api-bindings", "data-structures", "no-std", "development-tools::ffi"] 14 | keywords = ["internet-computer", "dfinity", "canister", "cdk"] 15 | include = ["src", "Cargo.toml", "LICENSE", "README.md", "build.rs"] 16 | # DO NOT change this field, not even to update the link, unless you are updating every existing version of the package. 17 | # Update the link by updating the links-pin tag. 18 | links = "ic-cdk-timers (canister_global_timer), see https://github.com/dfinity/cdk-rs/blob/links-pin/TROUBLESHOOTING.md" 19 | 20 | [dependencies] 21 | ic0.workspace = true 22 | ic-cdk-executor.workspace = true 23 | slotmap.workspace = true 24 | 25 | [dev-dependencies] 26 | ic-cdk.workspace = true 27 | 28 | [package.metadata.docs.rs] 29 | default-target = "wasm32-unknown-unknown" 30 | rustc-args = ["--cfg=docsrs"] 31 | -------------------------------------------------------------------------------- /ic-management-canister-types/README.md: -------------------------------------------------------------------------------- 1 | # ic-management-canister-types 2 | 3 | Types for calling [the IC management canister][1]. 4 | 5 | This module is a direct translation from its Candid interface description. 6 | 7 | [1]: https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-management-canister 8 | 9 | ## Correctness 10 | 11 | This crate ensures type definition correctness through the [`candid_equality.rs`](tests/candid_equality.rs) test. 12 | 13 | The test defines a dummy Canister covering all Management Canister entry points available for inter-canister calls. 14 | 15 | It then asserts the equality of the dummy canister's interface with the specified interface in [`ic.did`](tests/ic.did). 16 | 17 | The [`ic.did`](tests/ic.did) is sourced from the [Internet Computer Interface Specification](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-candid). 18 | 19 | Some methods are excluded (commented out) as follows: 20 | - Bitcoin API: These functionalities are planned to migrate from the Management Canister to the [Bitcoin Canister](https://github.com/dfinity/bitcoin-canister). 21 | - `fetch_canister_logs`: This method is only available for ingress messages (using an agent) and cannot be invoked in inter-canister calls. 22 | -------------------------------------------------------------------------------- /.github/workflows/conventional-commits.yml: -------------------------------------------------------------------------------- 1 | name: Check PR title 2 | 3 | on: 4 | pull_request_target: 5 | types: 6 | - opened 7 | - reopened 8 | - edited 9 | - synchronize 10 | 11 | concurrency: 12 | group: ${{ github.workflow }}-${{ github.ref }} 13 | cancel-in-progress: true 14 | 15 | jobs: 16 | check: 17 | name: conventional-pr-title:required 18 | runs-on: ubuntu-24.04 19 | steps: 20 | # Conventional commit patterns: 21 | # verb: description 22 | # verb!: description of breaking change 23 | # verb(scope): Description of change to $scope 24 | # verb(scope)!: Description of breaking change to $scope 25 | # verb: feat, fix, ... 26 | # scope: refers to the part of code being changed. E.g. " (accounts)" or " (accounts,canisters)" 27 | # !: Indicates that the PR contains a breaking change. 28 | - env: 29 | TITLE: ${{ github.event.pull_request.title }} 30 | run: | 31 | echo "PR title: $TITLE" 32 | if [[ "$TITLE" =~ ^(feat|fix|chore|build|ci|docs|style|refactor|perf|test)(\([-a-zA-Z0-9,]+\))?\!?\: ]]; then 33 | echo pass 34 | else 35 | echo "PR title does not match conventions" 36 | exit 1 37 | fi 38 | -------------------------------------------------------------------------------- /ic-cdk/tests/compile_fail/no_guard_function_for_lifecycle.stderr: -------------------------------------------------------------------------------- 1 | error: #[init] cannot have guard function(s). 2 | --> tests/compile_fail/no_guard_function_for_lifecycle.rs:7:8 3 | | 4 | 7 | #[init(guard = "guard_function")] 5 | | ^^^^^ 6 | 7 | error: #[pre_upgrade] cannot have guard function(s). 8 | --> tests/compile_fail/no_guard_function_for_lifecycle.rs:10:15 9 | | 10 | 10 | #[pre_upgrade(guard = "guard_function")] 11 | | ^^^^^ 12 | 13 | error: #[post_upgrade] cannot have guard function(s). 14 | --> tests/compile_fail/no_guard_function_for_lifecycle.rs:13:16 15 | | 16 | 13 | #[post_upgrade(guard = "guard_function")] 17 | | ^^^^^ 18 | 19 | error: #[heartbeat] cannot have guard function(s). 20 | --> tests/compile_fail/no_guard_function_for_lifecycle.rs:16:13 21 | | 22 | 16 | #[heartbeat(guard = "guard_function")] 23 | | ^^^^^ 24 | 25 | error: #[inspect_message] cannot have guard function(s). 26 | --> tests/compile_fail/no_guard_function_for_lifecycle.rs:19:19 27 | | 28 | 19 | #[inspect_message(guard = "guard_function")] 29 | | ^^^^^ 30 | 31 | error: #[on_low_wasm_memory] cannot have guard function(s). 32 | --> tests/compile_fail/no_guard_function_for_lifecycle.rs:22:22 33 | | 34 | 22 | #[on_low_wasm_memory(guard = "guard_function")] 35 | | ^^^^^ 36 | -------------------------------------------------------------------------------- /e2e-tests/src/bin/bitcoin_canister.rs: -------------------------------------------------------------------------------- 1 | use ic_cdk::bitcoin_canister::*; 2 | use ic_cdk::call::Error; 3 | use ic_cdk::update; 4 | 5 | /// A random Bitcoin address for testing. 6 | const BTC_ADDRESS: &str = "bcrt1qu58aj62urda83c00eylc6w34yl2s6e5rkzqet7"; 7 | 8 | #[update] 9 | async fn execute_non_query_methods(network: Network) { 10 | let arg = GetUtxosRequest { 11 | address: BTC_ADDRESS.to_string(), 12 | network, 13 | filter: Some(UtxosFilter::MinConfirmations(1)), 14 | }; 15 | let _response = bitcoin_get_utxos(&arg).await.unwrap(); 16 | 17 | let arg = GetBalanceRequest { 18 | network, 19 | address: BTC_ADDRESS.to_string(), 20 | min_confirmations: Some(1), 21 | }; 22 | let _balance = bitcoin_get_balance(&arg).await.unwrap(); 23 | 24 | let arg = GetCurrentFeePercentilesRequest { network }; 25 | let _percentiles = bitcoin_get_current_fee_percentiles(&arg).await.unwrap(); 26 | 27 | let arg = GetBlockHeadersRequest { 28 | network, 29 | start_height: 0, 30 | end_height: None, 31 | }; 32 | let _response = bitcoin_get_block_headers(&arg).await.unwrap(); 33 | 34 | let arg = SendTransactionRequest { 35 | transaction: vec![], 36 | network, 37 | }; 38 | let err = bitcoin_send_transaction(&arg).await.unwrap_err(); 39 | assert!(matches!(err, Error::CallRejected { .. })); 40 | } 41 | 42 | fn main() {} 43 | -------------------------------------------------------------------------------- /ic-cdk-timers/README.md: -------------------------------------------------------------------------------- 1 | Rust CDK Timers Library 2 | ======================= 3 | 4 | [![Documentation](https://docs.rs/ic-cdk-timers/badge.svg)](https://docs.rs/ic-cdk-timers/) 5 | [![Crates.io](https://img.shields.io/crates/v/ic-cdk-timers.svg)](https://crates.io/crates/ic-cdk-timers) 6 | [![License](https://img.shields.io/crates/l/ic-cdk-timers.svg)](https://github.com/dfinity/cdk-rs/blob/main/src/ic-cdk-timers/LICENSE) 7 | [![Downloads](https://img.shields.io/crates/d/ic-cdk-timers.svg)](https://crates.io/crates/ic-cdk-timers) 8 | [![CI](https://github.com/dfinity/cdk-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/dfinity/cdk-rs/actions/workflows/ci.yml) 9 | 10 | This crate provides a library to schedule multiple and periodic tasks on the Internet Computer. 11 | 12 | Example 13 | ------- 14 | 15 | In `Cargo.toml`: 16 | 17 | ```toml 18 | [dependencies] 19 | ic-cdk-timers = "1.0.0" 20 | ``` 21 | 22 | To schedule a one-shot task to be executed 1s later: 23 | 24 | ```rust 25 | ic_cdk_timers::set_timer(Duration::from_secs(1), async { ic_cdk::println!("Hello from the future!") }); 26 | ``` 27 | 28 | References 29 | ---------- 30 | 31 | 1. Internet Computer Developer Guide: [Periodic Tasks and Timers](https://internetcomputer.org/docs/current/developer-docs/backend/periodic-tasks) 32 | 2. Example: [Periodic Tasks and Timers](https://github.com/dfinity/examples/tree/master/rust/periodic_tasks) (compares timers and heartbeats). 33 | -------------------------------------------------------------------------------- /ic-cdk/src/api/management_canister/ecdsa/mod.rs: -------------------------------------------------------------------------------- 1 | //! Threshold ECDSA signing API. 2 | 3 | use crate::api::call::{CallResult, call, call_with_payment128}; 4 | use candid::Principal; 5 | 6 | mod types; 7 | pub use types::*; 8 | 9 | const SIGN_WITH_ECDSA_FEE: u128 = 26_153_846_153; 10 | 11 | /// Return a SEC1 encoded ECDSA public key for the given canister using the given derivation path. 12 | /// 13 | /// See [IC method `ecdsa_public_key`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-ecdsa_public_key). 14 | pub async fn ecdsa_public_key( 15 | arg: EcdsaPublicKeyArgument, 16 | ) -> CallResult<(EcdsaPublicKeyResponse,)> { 17 | call(Principal::management_canister(), "ecdsa_public_key", (arg,)).await 18 | } 19 | 20 | /// Return a new ECDSA signature of the given `message_hash` that can be separately verified against a derived ECDSA public key. 21 | /// 22 | /// See [IC method `sign_with_ecdsa`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-sign_with_ecdsa). 23 | /// 24 | /// This call requires cycles payment. 25 | /// This method handles the cycles cost under the hood. 26 | /// Check [Threshold signatures](https://internetcomputer.org/docs/current/references/t-sigs-how-it-works) for more details. 27 | pub async fn sign_with_ecdsa(arg: SignWithEcdsaArgument) -> CallResult<(SignWithEcdsaResponse,)> { 28 | call_with_payment128( 29 | Principal::management_canister(), 30 | "sign_with_ecdsa", 31 | (arg,), 32 | SIGN_WITH_ECDSA_FEE, 33 | ) 34 | .await 35 | } 36 | -------------------------------------------------------------------------------- /ic-cdk-bindgen/src/templates/dynamic_callee.hbs: -------------------------------------------------------------------------------- 1 | use {{candid_crate}}::{CandidType, Deserialize, Principal}; 2 | use ic_cdk::call::{Call, CallResult}; 3 | // START -- Types 4 | {{type_defs}} 5 | // END -- Types 6 | /// Get the canister ID via the environment variable `{{env_var_name}}`. 7 | #[inline] 8 | pub fn __get_canister_id() -> Principal { 9 | if !::ic_cdk::api::env_var_name_exists("{{env_var_name}}") { 10 | panic!("env var `{{env_var_name}}` is not set. Canister controller can set it using tools like icp-cli."); 11 | } 12 | let canister_id_str = ::ic_cdk::api::env_var_value("{{env_var_name}}"); 13 | Principal::from_text(&canister_id_str).unwrap_or_else(|e| { 14 | panic!("failed to parse Principal from env var `{{env_var_name}}`, value `{}`: {}", canister_id_str, e) 15 | }) 16 | } 17 | // START -- Methods 18 | {{#if methods}} 19 | {{#each methods}} 20 | {{#each this.docs}} 21 | {{../../doc_comment_prefix}}{{this}} 22 | {{/each}} 23 | pub async fn {{this.name}}({{#each this.args}}{{this.0}}: &{{this.1}}, {{/each}}) -> CallResult<{{#if (eq (len this.rets) 1)}}{{this.rets.0}}{{else}}({{#each this.rets}}{{this}}{{#unless @last}}, {{/unless}}{{/each}}){{/if}}> { 24 | Ok(Call::bounded_wait(__get_canister_id(), "{{escape_debug this.original_name}}"){{#if (eq (len this.args) 0)}}{{else}}{{#if (eq (len this.args) 1)}}.with_arg({{this.args.0.0}}){{else}}.with_args(&({{#each this.args}}{{this.0}}{{#unless @last}}, {{/unless}}{{/each}})){{/if}}{{/if}}.await?.candid()?) 25 | } 26 | {{/each}} 27 | {{/if}} 28 | // END -- Methods 29 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "ic-cdk", 4 | "ic-cdk-bindgen", 5 | "ic-cdk-executor", 6 | "ic-cdk-macros", 7 | "ic-cdk-timers", 8 | "ic-management-canister-types", 9 | "ic0", 10 | "library/*", 11 | "e2e-tests", 12 | ] 13 | resolver = "3" 14 | exclude = ["candid-extractor"] 15 | 16 | [workspace.package] 17 | authors = ["DFINITY Stiftung "] 18 | edition = "2024" 19 | repository = "https://github.com/dfinity/cdk-rs" 20 | # MSRV 21 | # Avoid updating this field unless we use new Rust features. 22 | # Sync with the `toolchain` field when setting up Rust toolchain in ci.yml msrv job. 23 | rust-version = "1.85.0" 24 | license = "Apache-2.0" 25 | 26 | [profile.canister-release] 27 | inherits = "release" 28 | debug = false 29 | panic = "abort" 30 | lto = true 31 | opt-level = 'z' 32 | 33 | [workspace.dependencies] 34 | ic0 = { path = "ic0", version = "1.0.1" } 35 | ic-cdk = { path = "ic-cdk", version = "0.19.0" } 36 | ic-cdk-bindgen = { path = "ic-cdk-bindgen", version = "0.2.0-alpha.2" } 37 | ic-cdk-timers = { path = "ic-cdk-timers", version = "1.0.0" } 38 | ic-cdk-executor = { path = "ic-cdk-executor", version = "2.0.0" } 39 | ic-management-canister-types = { path = "ic-management-canister-types", version = "0.5.0" } 40 | 41 | candid = "0.10.18" # sync with the doc comment in ic-cdk/README.md 42 | candid_parser = "0.2.1" 43 | futures = "0.3" 44 | hex = "0.4" 45 | ic_principal = "0.1" 46 | quote = "1" 47 | serde = "1" 48 | serde_bytes = "0.11" 49 | sha2 = "0.10" 50 | slotmap = "1" 51 | syn = "2" 52 | thiserror = "2.0" 53 | -------------------------------------------------------------------------------- /ic-cdk/tests/pass/blank_methods.rs: -------------------------------------------------------------------------------- 1 | use ic_cdk::{ 2 | heartbeat, init, inspect_message, on_low_wasm_memory, post_upgrade, pre_upgrade, query, update, 3 | }; 4 | 5 | #[init] 6 | fn init() {} 7 | 8 | #[pre_upgrade] 9 | fn pre_upgrade() {} 10 | 11 | #[post_upgrade] 12 | fn post_upgrade() {} 13 | 14 | #[update] 15 | fn update() {} 16 | 17 | #[update(hidden)] 18 | fn update_hidden() {} 19 | 20 | #[update(hidden = true)] 21 | fn update_hidden_true() {} 22 | 23 | #[update(hidden = false)] 24 | fn update_hidden_false() {} 25 | 26 | #[update(manual_reply)] 27 | fn update_manual_reply() {} 28 | 29 | #[update(manual_reply = true)] 30 | fn update_manual_reply_true() {} 31 | 32 | #[update(manual_reply = false)] 33 | fn update_manual_reply_false() {} 34 | 35 | #[query] 36 | fn query() {} 37 | 38 | #[query(hidden)] 39 | fn query_hidden() {} 40 | 41 | #[query(hidden = true)] 42 | fn query_hidden_true() {} 43 | 44 | #[query(hidden = false)] 45 | fn query_hidden_false() {} 46 | 47 | #[query(manual_reply)] 48 | fn query_manual_reply() {} 49 | 50 | #[query(manual_reply = true)] 51 | fn query_manual_reply_true() {} 52 | 53 | #[query(manual_reply = false)] 54 | fn query_manual_reply_false() {} 55 | 56 | #[query(composite)] 57 | fn query_composite() {} 58 | 59 | #[query(composite = true)] 60 | fn query_composite_true() {} 61 | 62 | #[query(composite = false)] 63 | fn query_composite_false() {} 64 | 65 | #[heartbeat] 66 | fn heartbeat() {} 67 | 68 | #[inspect_message] 69 | fn inspect_message() {} 70 | 71 | #[on_low_wasm_memory] 72 | fn on_low_wasm_memory() {} 73 | 74 | fn main() {} 75 | -------------------------------------------------------------------------------- /library/ic-certified-map/src/hashtree/test.rs: -------------------------------------------------------------------------------- 1 | use super::{ 2 | HashTree::{Empty, Leaf}, 3 | fork, labeled, 4 | }; 5 | use std::borrow::Cow; 6 | 7 | //─┬─┬╴"a" ─┬─┬╴"x" ─╴"hello" 8 | // │ │ │ └╴Empty 9 | // │ │ └╴ "y" ─╴"world" 10 | // │ └╴"b" ──╴"good" 11 | // └─┬╴"c" ──╴Empty 12 | // └╴"d" ──╴"morning" 13 | #[test] 14 | fn test_public_spec_example() { 15 | let t = fork( 16 | fork( 17 | labeled( 18 | b"a", 19 | fork( 20 | fork(labeled(b"x", Leaf(Cow::Borrowed(b"hello"))), Empty), 21 | labeled(b"y", Leaf(Cow::Borrowed(b"world"))), 22 | ), 23 | ), 24 | labeled(b"b", Leaf(Cow::Borrowed(b"good"))), 25 | ), 26 | fork( 27 | labeled(b"c", Empty), 28 | labeled(b"d", Leaf(Cow::Borrowed(b"morning"))), 29 | ), 30 | ); 31 | 32 | let root = t.reconstruct(); 33 | assert_eq!( 34 | hex::encode(&root[..]), 35 | "eb5c5b2195e62d996b84c9bcc8259d19a83786a2f59e0878cec84c811f669aa0".to_string() 36 | ); 37 | 38 | let cbor = serde_cbor::to_vec(&t).unwrap(); 39 | let encoded = hex::encode(&cbor[..]); 40 | assert_eq!( 41 | encoded, 42 | "8301830183024161830183018302417882034568656c6c6f810083024179820345776f726c6483024162820344676f6f648301830241638100830241648203476d6f726e696e67".to_string()); 43 | 44 | let decoded = hex::decode(encoded).unwrap(); 45 | let de: super::HashTree<'_> = serde_cbor::from_slice(&decoded).unwrap(); 46 | 47 | assert_eq!(de.reconstruct(), root); 48 | assert_eq!(serde_cbor::to_vec(&de).unwrap(), cbor); 49 | } 50 | -------------------------------------------------------------------------------- /ic-cdk/src/api/management_canister/schnorr/mod.rs: -------------------------------------------------------------------------------- 1 | //! Threshold Schnorr signing API. 2 | 3 | use crate::api::call::{CallResult, call, call_with_payment128}; 4 | use candid::Principal; 5 | 6 | mod types; 7 | pub use types::*; 8 | 9 | // Source: https://internetcomputer.org/docs/current/references/t-sigs-how-it-works/#fees-for-the-t-schnorr-production-key 10 | const SIGN_WITH_SCHNORR_FEE: u128 = 26_153_846_153; 11 | 12 | /// Return a SEC1 encoded Schnorr public key for the given canister using the given derivation path. 13 | /// 14 | /// See [IC method `schnorr_public_key`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-schnorr_public_key). 15 | pub async fn schnorr_public_key( 16 | arg: SchnorrPublicKeyArgument, 17 | ) -> CallResult<(SchnorrPublicKeyResponse,)> { 18 | call( 19 | Principal::management_canister(), 20 | "schnorr_public_key", 21 | (arg,), 22 | ) 23 | .await 24 | } 25 | 26 | /// Return a new Schnorr signature of the given message that can be separately verified against a derived Schnorr public key. 27 | /// 28 | /// See [IC method `sign_with_schnorr`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-sign_with_schnorr). 29 | /// 30 | /// This call requires cycles payment. 31 | /// This method handles the cycles cost under the hood. 32 | /// Check [Threshold signatures](https://internetcomputer.org/docs/current/references/t-sigs-how-it-works) for more details. 33 | pub async fn sign_with_schnorr( 34 | arg: SignWithSchnorrArgument, 35 | ) -> CallResult<(SignWithSchnorrResponse,)> { 36 | call_with_payment128( 37 | Principal::management_canister(), 38 | "sign_with_schnorr", 39 | (arg,), 40 | SIGN_WITH_SCHNORR_FEE, 41 | ) 42 | .await 43 | } 44 | -------------------------------------------------------------------------------- /ic-cdk-executor/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## Unreleased 9 | 10 | ## [2.0.0] - 2025-11-13 11 | 12 | ### Changed 13 | 14 | - Breaking: The API has been significantly revised. 15 | - `spawn` has been replaced with `spawn_protected` and `spawn_migratory`. Protected tasks are cancelled when the method ends. 16 | - Contexts are now 'tracking', meaning that they know when canister methods start and end. Calls to `ic0.call_*` must be accompanied by `extend_current_method_context`; `in_callback_executor_context_for` takes this value. 17 | - Cancellation is no longer done per-task with the waker, but per-method with the function `cancel_all_tasks_attached_to_current_method`. 18 | 19 | ## [1.0.2] - 2025-08-18 20 | 21 | ### Changed 22 | 23 | - Moved the panic handler logic into the executor. 24 | 25 | ### Fixed 26 | 27 | - Removed a panic from the case where a callback context closure doesn't do anything. The only way this usually occurs is if you upgrade a canister without stopping it first. A message has been added warning you not to do this. 28 | 29 | ## [1.0.1] - 2025-06-30 30 | 31 | ### Changed 32 | 33 | - Added a check against spawning tasks during trap recovery. 34 | - Added a check against invoking wakers outside an executor context. 35 | 36 | ### Fixed 37 | 38 | - Removed a panic when a dangling waker is invoked. 39 | 40 | ## [1.0.0] - 2025-05-13 41 | 42 | This release is a copy-paste of the executor from ic-cdk 0.18.1. 43 | 44 | ## [0.1.0] - 2025-05-13 45 | 46 | This release is a copy-paste of the executor from ic-cdk 0.17.1. 47 | -------------------------------------------------------------------------------- /ic-cdk-bindgen/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [unreleased] 8 | 9 | ### New API Design 10 | 11 | The `ic-cdk-bindgen` crate introduces a completely redesigned API to integrate seamlessly with `ic-cdk` v0.18 and later: 12 | - The new API centers around the `Config` struct, requiring explicit specification of canister name and candid path. 13 | - The bindgen now supports two modes: 14 | - `static_callee`: The canister ID is known at compile time. 15 | - `dynamic_callee`: The canister ID will be fetched via ICP environment variables at runtime. 16 | - The "Type Selector" config can be set to customize how Candid types are translated to Rust types. 17 | - Removed implicit handling of `dfx` environment variables. See the "Use with `dfx`" section in the crate documentation for more info. 18 | 19 | ## [0.1.3] - 2024-02-27 20 | 21 | ### Added 22 | 23 | - Resolve CANISTER_CANDID_PATH and CANISTER_ID from standardized environment variables (uppercase canister names). (#467) 24 | - The support for legacy (non-uppercase) env vars is kept. 25 | - It will be removed in next major release (v0.2). 26 | 27 | ## [0.1.2] - 2023-11-23 28 | 29 | ### Changed 30 | 31 | - Change `candid` dependency to the new `candid_parser` library. (#448) 32 | More details here: https://github.com/dfinity/candid/blob/master/Changelog.md#2023-11-16-rust-0100 33 | 34 | ## [0.1.1] - 2023-09-18 35 | 36 | ### Changed 37 | 38 | - Update `candid` dependency to 0.9.6 which change the Rust bindings. (#424) 39 | 40 | ## [0.1.0] - 2023-07-13 41 | 42 | ### Added 43 | 44 | - First release. (#416) 45 | -------------------------------------------------------------------------------- /library/ic-certified-map/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## Unreleased 8 | 9 | ## [0.4.1] - 2025-09-05 10 | 11 | ### Added 12 | - Implement `CandidType`, `Serialize`, and `Deserialize` for the `RbTree`. 13 | - Implement `Deserialize` for the `HashTree`. 14 | 15 | ## [0.4.0] - 2023-07-13 16 | 17 | ### Changed 18 | - Upgrade `ic-cdk` to v0.10 and `candid` to v0.9. 19 | 20 | ## [0.3.4] - 2023-03-01 21 | ### Added 22 | - Derive common traits for structs. 23 | 24 | ## [0.3.3] - 2023-02-22 25 | ### Fixed 26 | - Update links in doc. 27 | 28 | ## [0.3.2] - 2022-11-10 29 | ### Changed 30 | - Make `RbTree::new` and `RbTree::is_empty` both `const`. 31 | 32 | ## [0.3.1] - 2022-09-16 33 | ### Changed 34 | - Updated `sha2` dependency. 35 | 36 | ## [0.3.0] - 2022-01-13 37 | ### Added 38 | - `RbTree::iter()` method. 39 | - impls of `Clone`, `PartialEq`, `Eq`, `PartialOrd`, `Ord`, `FromIterator`, and `Debug` for `RbTree`. 40 | 41 | ## [0.2.0] - 2021-09-16 42 | ### Added 43 | - `RbTree::value_range()` method to get a witness for a range of keys with values. 44 | 45 | ### Changed 46 | - RbTree::key_range() method returns tighter key bounds which reduces the size of witnesses. 47 | - Updated the version of candid from `0.6.19` to `0.7.1` ([#72](https://github.com/dfinity/cdk-rs/pull/72)). 48 | - Hash tree leaves can now hold both references and values ([#121](https://github.com/dfinity/cdk-rs/issues/121)). 49 | This is a BREAKING CHANGE, some clients might need to slightly change and recompile their code. 50 | 51 | ## [0.1.0] - 2021-05-04 52 | ### Added 53 | * Initial release of the library. 54 | -------------------------------------------------------------------------------- /ic-cdk/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ic-cdk" 3 | version = "0.19.0" # sync with ic-cdk-macros and the doc comment in README.md 4 | authors.workspace = true 5 | edition.workspace = true 6 | license.workspace = true 7 | rust-version.workspace = true 8 | repository.workspace = true 9 | description = "Canister Developer Kit for the Internet Computer." 10 | homepage = "https://docs.rs/ic-cdk" 11 | documentation = "https://docs.rs/ic-cdk" 12 | readme = "README.md" 13 | categories = ["api-bindings", "data-structures", "no-std", "development-tools::ffi"] 14 | keywords = ["internet-computer", "types", "dfinity", "canister", "cdk"] 15 | include = ["src", "Cargo.toml", "LICENSE", "README.md"] 16 | 17 | [dependencies] 18 | candid.workspace = true 19 | ic0.workspace = true 20 | ic-cdk-executor.workspace = true 21 | # Pin ic-cdk-macros to a specific version. 22 | # This actually create a 1-to-1 mapping between ic-cdk and ic-cdk-macros. 23 | # Dependents won't accidentaly upgrading ic-cdk-macros only but not ic-cdk. 24 | # ic-cdk-macros is a hidden dependency, re-exported by ic-cdk. 25 | # It should not be included by users direcly. 26 | ic-cdk-macros = { path = "../ic-cdk-macros", version = "=0.19.0" } 27 | ic-error-types = "0.2.0" 28 | ic-management-canister-types.workspace = true 29 | pin-project-lite = "0.2.16" 30 | serde.workspace = true 31 | serde_bytes.workspace = true 32 | slotmap.workspace = true 33 | thiserror.workspace = true 34 | 35 | [dev-dependencies] 36 | anyhow = "1" 37 | candid_parser.workspace = true 38 | futures = "0.3" 39 | rstest = "0.12.0" 40 | trybuild = "1.0" 41 | 42 | [features] 43 | transform-closure = [] 44 | 45 | [package.metadata.docs.rs] 46 | features = ["transform-closure"] 47 | default-target = "wasm32-unknown-unknown" 48 | rustdoc-args = ["--cfg=docsrs"] 49 | -------------------------------------------------------------------------------- /ic-cdk/src/api/management_canister/mod.rs: -------------------------------------------------------------------------------- 1 | //! Functions and types for calling [the IC management canister][1]. 2 | //! 3 | //! This module is a direct translation from the [interface description][2]. 4 | //! 5 | //! The functions and types defined in this module serves these purposes: 6 | //! * Make it easy to construct correct request data. 7 | //! * Handle the response ergonomically. 8 | //! * For those calls require cycles payments, the cycles amount is an explicit argument. 9 | //! 10 | //! [1]: https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-management-canister 11 | //! [2]: https://internetcomputer.org/assets/files/ic-a45d11feb0ba0494055083f9d2d21ddf.did 12 | #![allow(deprecated)] 13 | #[deprecated( 14 | since = "0.18.0", 15 | note = "The `api::management_canister::bitcoin` module is deprecated. Please use the `bitcoin_canister` module at the crate root." 16 | )] 17 | pub mod bitcoin; 18 | #[deprecated( 19 | since = "0.18.0", 20 | note = "The `api::management_canister::ecdsa` module is deprecated. Please use the `management_canister` module at the crate root." 21 | )] 22 | pub mod ecdsa; 23 | #[deprecated( 24 | since = "0.18.0", 25 | note = "The `api::management_canister::http_request` module is deprecated. Please use the `management_canister` module at the crate root." 26 | )] 27 | pub mod http_request; 28 | #[deprecated( 29 | since = "0.18.0", 30 | note = "The `api::management_canister::main` module is deprecated. Please use the `management_canister` module at the crate root." 31 | )] 32 | pub mod main; 33 | #[deprecated( 34 | since = "0.18.0", 35 | note = "The `api::management_canister::provisional` module is deprecated. Please use the `management_canister` module at the crate root." 36 | )] 37 | pub mod provisional; 38 | #[deprecated( 39 | since = "0.18.0", 40 | note = "The `api::management_canister::schnorr` module is deprecated. Please use the `management_canister` module at the crate root." 41 | )] 42 | pub mod schnorr; 43 | -------------------------------------------------------------------------------- /e2e-tests/src/bin/chunk.rs: -------------------------------------------------------------------------------- 1 | use candid::Principal; 2 | use ic_cdk::management_canister::{ 3 | clear_chunk_store, create_canister_with_extra_cycles, install_chunked_code, stored_chunks, 4 | upload_chunk, CanisterInstallMode, ChunkHash, ClearChunkStoreArgs, CreateCanisterArgs, 5 | InstallChunkedCodeArgs, StoredChunksArgs, UploadChunkArgs, 6 | }; 7 | use ic_cdk::update; 8 | 9 | #[update] 10 | async fn call_create_canister() -> Principal { 11 | create_canister_with_extra_cycles(&CreateCanisterArgs::default(), 1_000_000_000_000u128) 12 | .await 13 | .unwrap() 14 | .canister_id 15 | } 16 | 17 | #[update] 18 | async fn call_upload_chunk(canister_id: Principal, chunk: Vec) -> Vec { 19 | let arg = UploadChunkArgs { 20 | canister_id, 21 | chunk: chunk.clone(), 22 | }; 23 | upload_chunk(&arg).await.unwrap().hash 24 | } 25 | 26 | #[update] 27 | async fn call_stored_chunks(canister_id: Principal) -> Vec> { 28 | let arg = StoredChunksArgs { canister_id }; 29 | let hashes = stored_chunks(&arg).await.unwrap(); 30 | hashes.into_iter().map(|v| v.hash).collect() 31 | } 32 | 33 | #[update] 34 | async fn call_clear_chunk_store(canister_id: Principal) { 35 | let arg = ClearChunkStoreArgs { canister_id }; 36 | clear_chunk_store(&arg).await.unwrap(); 37 | } 38 | 39 | #[update] 40 | async fn call_install_chunked_code( 41 | canister_id: Principal, 42 | chunk_hashes_list: Vec>, 43 | wasm_module_hash: Vec, 44 | ) { 45 | let chunk_hashes_list = chunk_hashes_list 46 | .iter() 47 | .map(|v| ChunkHash { hash: v.clone() }) 48 | .collect(); 49 | let arg = InstallChunkedCodeArgs { 50 | mode: CanisterInstallMode::Install, 51 | target_canister: canister_id, 52 | store_canister: None, 53 | chunk_hashes_list, 54 | wasm_module_hash, 55 | arg: vec![], 56 | }; 57 | install_chunked_code(&arg).await.unwrap(); 58 | } 59 | 60 | fn main() {} 61 | -------------------------------------------------------------------------------- /e2e-tests/tests/management_canister.rs: -------------------------------------------------------------------------------- 1 | mod test_utilities; 2 | use test_utilities::{cargo_build_canister, pic_base, update}; 3 | 4 | #[test] 5 | fn test_management_canister() { 6 | let wasm = cargo_build_canister("management_canister"); 7 | // Setup pocket-ic with an II subnet which is required by the "provisional" test. 8 | let pic = pic_base().with_ii_subnet().build(); 9 | 10 | let canister_id = pic.create_canister(); 11 | let subnet_id = pic.get_subnet(canister_id).unwrap(); 12 | pic.add_cycles(canister_id, 10_000_000_000_000u128); // 10 T 13 | pic.install_canister(canister_id, wasm.clone(), vec![], None); 14 | let () = update(&pic, canister_id, "basic", ()).unwrap(); 15 | let () = update(&pic, canister_id, "env_var", ()).unwrap(); 16 | let () = update(&pic, canister_id, "ecdsa", ()).unwrap(); 17 | let () = update(&pic, canister_id, "schnorr", ()).unwrap(); 18 | let () = update(&pic, canister_id, "metrics", (subnet_id,)).unwrap(); 19 | let () = update(&pic, canister_id, "subnet", (subnet_id,)).unwrap(); 20 | let () = update(&pic, canister_id, "snapshots", ()).unwrap(); 21 | 22 | // provisional 23 | // Install the test canister on the II subnet, so that it can provisional create canisters on the same subnet. 24 | let ii_subnet_id = pic.topology().get_ii().unwrap(); 25 | let canister_id_on_ii = pic.create_canister_on_subnet(None, None, ii_subnet_id); 26 | pic.add_cycles(canister_id_on_ii, 10_000_000_000_000u128); // 10 T 27 | pic.install_canister(canister_id_on_ii, wasm, vec![], None); 28 | let () = update(&pic, canister_id_on_ii, "provisional", ()).unwrap(); 29 | 30 | // vetkd 31 | const VETKD_TRANSPORT_SECRET_KEY_SEED: [u8; 32] = [13; 32]; 32 | let transport_key = 33 | ic_vetkd_utils::TransportSecretKey::from_seed(VETKD_TRANSPORT_SECRET_KEY_SEED.to_vec()) 34 | .expect("Failed to generate transport secret key"); 35 | let transport_public_key = transport_key.public_key(); 36 | let () = update(&pic, canister_id, "vetkd", (transport_public_key,)).unwrap(); 37 | } 38 | -------------------------------------------------------------------------------- /ic-cdk/tests/compile_fail/lifecycle_functions_should_have_no_return.stderr: -------------------------------------------------------------------------------- 1 | error: #[init] function cannot have a return value. 2 | --> tests/compile_fail/lifecycle_functions_should_have_no_return.rs:3:1 3 | | 4 | 3 | #[init] 5 | | ^^^^^^^ 6 | | 7 | = note: this error originates in the attribute macro `init` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | 9 | error: #[pre_upgrade] function cannot have a return value. 10 | --> tests/compile_fail/lifecycle_functions_should_have_no_return.rs:6:1 11 | | 12 | 6 | #[pre_upgrade] 13 | | ^^^^^^^^^^^^^^ 14 | | 15 | = note: this error originates in the attribute macro `pre_upgrade` (in Nightly builds, run with -Z macro-backtrace for more info) 16 | 17 | error: #[post_upgrade] function cannot have a return value. 18 | --> tests/compile_fail/lifecycle_functions_should_have_no_return.rs:9:1 19 | | 20 | 9 | #[post_upgrade] 21 | | ^^^^^^^^^^^^^^^ 22 | | 23 | = note: this error originates in the attribute macro `post_upgrade` (in Nightly builds, run with -Z macro-backtrace for more info) 24 | 25 | error: #[heartbeat] function cannot have a return value. 26 | --> tests/compile_fail/lifecycle_functions_should_have_no_return.rs:12:1 27 | | 28 | 12 | #[heartbeat] 29 | | ^^^^^^^^^^^^ 30 | | 31 | = note: this error originates in the attribute macro `heartbeat` (in Nightly builds, run with -Z macro-backtrace for more info) 32 | 33 | error: #[inspect_message] function cannot have a return value. 34 | --> tests/compile_fail/lifecycle_functions_should_have_no_return.rs:15:1 35 | | 36 | 15 | #[inspect_message] 37 | | ^^^^^^^^^^^^^^^^^^ 38 | | 39 | = note: this error originates in the attribute macro `inspect_message` (in Nightly builds, run with -Z macro-backtrace for more info) 40 | 41 | error: #[on_low_wasm_memory] function cannot have a return value. 42 | --> tests/compile_fail/lifecycle_functions_should_have_no_return.rs:18:1 43 | | 44 | 18 | #[on_low_wasm_memory] 45 | | ^^^^^^^^^^^^^^^^^^^^^ 46 | | 47 | = note: this error originates in the attribute macro `on_low_wasm_memory` (in Nightly builds, run with -Z macro-backtrace for more info) 48 | -------------------------------------------------------------------------------- /ic-cdk-macros/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | This file will no longer be updated. 4 | 5 | Please check [`ic-cdk` changelog](../ic-cdk/CHANGELOG.md). 6 | 7 | --- 8 | 9 | All notable changes to this project will be documented in this file. 10 | 11 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 12 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 13 | 14 | ### Fixed 15 | 16 | - `cargo build` should no longer give a confusing linkage error on Linux. 17 | 18 | ## [0.13.2] - 2024-04-08 19 | 20 | ### Changed 21 | 22 | - `ic-cdk-macros` will have the same version as the `ic-cdk`. 23 | 24 | ## [0.9.0] - 2024-03-01 25 | 26 | ### Fixed 27 | 28 | - The change in yanked version v0.8.5 contains breaking change. 29 | 30 | ## [0.8.5] - 2024-03-01 (yanked) 31 | 32 | ### Added 33 | 34 | - Allow setting decoding quota for canister entry points and inter-canister calls. (#465) 35 | 36 | ## [0.8.4] - 2024-01-12 37 | 38 | ### Fixed 39 | 40 | - The README file is now more informative and used as the front page of the doc site. 41 | 42 | ## [0.8.3] - 2023-12-13 43 | 44 | ### Added 45 | 46 | - `#[query(hidden = true)]`/`#[update(hidden = true)]` attribute to exclude exporting certain entry points in Candid generated by `export_candid!()`. (#451) 47 | 48 | ## [0.8.2] - 2023-11-23 49 | 50 | ### Changed 51 | 52 | - Upgrade `candid` to `0.10`. (#448) 53 | 54 | ## [0.8.1] - 2023-10-02 55 | 56 | ### Fixed 57 | 58 | - Macros no longer use global state in the names of functions. JetBrains IDEs should no longer produce spurious errors. (#430) 59 | 60 | ## [0.8.0] - 2023-09-18 61 | 62 | ### Changed 63 | 64 | - Remove `export_candid` feature. (#424) 65 | 66 | ### Fixed 67 | 68 | - Export composite_query to Candid. (#419) 69 | 70 | ## [0.7.1] - 2023-07-27 71 | 72 | ### Fixed 73 | 74 | - Only update/query macros can take guard function. (#417) 75 | 76 | ## [0.7.0] - 2023-07-13 77 | 78 | ### Added 79 | 80 | - `export_candid` macro. (#386) 81 | 82 | ### Changed 83 | 84 | - Remove `import` macro. (#390) 85 | 86 | ## [0.6.10] - 2023-03-01 87 | 88 | ### Changed 89 | 90 | - Update lint settings. 91 | -------------------------------------------------------------------------------- /ic-cdk-macros/README.md: -------------------------------------------------------------------------------- 1 | [![Documentation](https://docs.rs/ic-cdk-macros/badge.svg)](https://docs.rs/ic-cdk-macros/) 2 | [![Crates.io](https://img.shields.io/crates/v/ic-cdk-macros.svg)](https://crates.io/crates/ic-cdk-macros) 3 | [![License](https://img.shields.io/crates/l/ic-cdk-macros.svg)](https://github.com/dfinity/cdk-rs/blob/main/LICENSE) 4 | [![Downloads](https://img.shields.io/crates/d/ic-cdk-macros.svg)](https://crates.io/crates/ic-cdk-macros) 5 | [![CI](https://github.com/dfinity/cdk-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/dfinity/cdk-rs/actions/workflows/ci.yml) 6 | 7 | # ic-cdk-macros 8 | 9 | This crate contains a collection of procedural macros that are utilized within the `ic-cdk` crate. 10 | 11 | The macros are re-exported in `ic-cdk`, and you can find their documentation [there](https://docs.rs/ic-cdk/latest/ic_cdk). 12 | 13 | --- 14 | 15 | The macros fall into two categories: 16 | 17 | * To register functions as canister entry points 18 | * To export Candid definitions 19 | 20 | ## Register functions as canister entry points 21 | 22 | These macros are directly related to the [Internet Computer Specification](https://internetcomputer.org/docs/current/references/ic-interface-spec#entry-points). 23 | 24 | * [`init`](https://docs.rs/ic-cdk/latest/ic_cdk/attr.init.html) 25 | * [`pre_upgrade`](https://docs.rs/ic-cdk/latest/ic_cdk/attr.pre_upgrade.html) 26 | * [`post_upgrade`](https://docs.rs/ic-cdk/latest/ic_cdk/attr.post_upgrade.html) 27 | * [`inspect_message`](https://docs.rs/ic-cdk/latest/ic_cdk/attr.inspect_message.html) 28 | * [`heartbeat`](https://docs.rs/ic-cdk/latest/ic_cdk/attr.heartbeat.html) 29 | * [`on_low_wasm_memory`](https://docs.rs/ic-cdk/latest/ic_cdk/attr.on_low_wasm_memory.html) 30 | * [`update`](https://docs.rs/ic-cdk/latest/ic_cdk/attr.update.html) 31 | * [`query`](https://docs.rs/ic-cdk/latest/ic_cdk/attr.query.html) 32 | 33 | ## Export Candid definitions 34 | 35 | * [`export_candid`](https://docs.rs/ic-cdk/latest/ic_cdk/macro.export_candid.html) 36 | 37 | Check [Generating Candid files for Rust canisters](https://internetcomputer.org/docs/current/developer-docs/backend/candid/generating-candid/) for more details. 38 | -------------------------------------------------------------------------------- /ic-cdk-executor/README.md: -------------------------------------------------------------------------------- 1 | # `ic-cdk-executor` 2 | 3 | An async executor for [`ic-cdk`](https://docs.rs/ic-cdk). Most users should not use this crate directly. It is useful primarily for those who are writing their own CDK or a runtime host for non-Rust languages. 4 | 5 | ## Contexts 6 | 7 | The expected boilerplate for a canister method or other entrypoint (*not* including callbacks) looks like this: 8 | 9 | ```rust 10 | pub extern "C" fn function() { 11 | in_tracking_executor_context(|| { 12 | // method goes here 13 | }); 14 | } 15 | ``` 16 | 17 | The `in_tracking_executor_context` function permits you to call `spawn_*` functions. As little code as possible 18 | should exist outside the block, because `in_tracking_executor_context` additionally sets up the panic handler. 19 | 20 | The above applies to update contexts. Query contexts, including `inspect_message`, should use 21 | `in_tracking_query_executor_context`. 22 | 23 | The expected boilerplate for an inter-canister call callback looks like this: 24 | 25 | ```rust 26 | unsafe extern "C" fn callback(env: usize) { 27 | let method = unpack_env(env); 28 | in_callback_executor_context_for(method, || { 29 | // wake the call future 30 | }); 31 | } 32 | unsafe extern "C" fn cleanup(env: usize) { 33 | let method = unpack_env(env); 34 | in_trap_recovery_context_for(method, || { 35 | cancel_all_tasks_attached_to_current_method(); 36 | }); 37 | } 38 | ``` 39 | 40 | In async contexts, all scheduled tasks are run *after* the closure passed to the context function 41 | returns, but *before* the context function itself returns. 42 | 43 | The `method` parameter must be retrieved *before* making inter-canister calls via the `extend_current_method_context` 44 | function. Calling this function from the callback instead will trap. 45 | 46 | ## Protection 47 | 48 | Tasks can be either *protected* or *migratory*. Protected tasks are attached to the method that spawned them, 49 | when awoken will not resume until that method continues, and will be canceled if the method returns before they complete. 50 | Migratory tasks are not attached to any method, and will resume in whatever method wakes them. 51 | -------------------------------------------------------------------------------- /TROUBLESHOOTING.md: -------------------------------------------------------------------------------- 1 | # Troubleshooting 2 | 3 | ## I was linked here by a Cargo error! 4 | 5 | If your Cargo command produces the following error: 6 | 7 | ``` 8 | error: failed to select a version for `ic-cdk-executor`. 9 | ... required by package `yourcrate v0.1.0 (/Users/you/yourcrate)` 10 | versions that meet the requirements `0.1.0` are: 0.1.0 11 | 12 | the package `ic-cdk-executor` links to the native library `ic-cdk async executor`, but it conflicts with a previous package which links to `ic-cdk async executor` as well: 13 | package `ic-cdk-executor v1.0.0` 14 | ... which satisfies dependency `ic-cdk-executor = "^1.0.0` of package `someothercrate v0.1.0` 15 | Only one package in the dependency graph may specify the same links value. This helps ensure that only one copy of a native library is linked in the final binary. Try to adjust your dependencies so that only one package uses the `links = "ic-cdk async executor"` value. For more information, see https://doc.rust-lang.org/cargo/reference/resolver.html#links. 16 | 17 | failed to select a version for `ic-cdk-executor` which could resolve this conflict 18 | ``` 19 | 20 | You have two incompatible versions of `ic-cdk` (or `ic-cdk-timers`) in your dependency tree. There are two common reasons for this. 21 | 22 | First, a dependency may be using an older (or newer) version of the CDK. Many versions of `ic-cdk` are compatible with each other, but versions 0.17 and earlier are incompatible with version 0.18, and 0.18 is incompatible with 0.19 or later. `ic-cdk-timers` does not have non-semver compatibility and any two versions are incompatible. In either case you will have to wait for those dependencies to update, or patch them yourself. 23 | 24 | Second, a dependency may be using a nominally compatible version of the CDK, but you are using a GitHub prerelease of the CDK with `ic-cdk = { git =`. Git dependencies are automatically incompatible with everything, even if nothing changed. You will need to create a [patch table](https://doc.rust-lang.org/cargo/reference/overriding-dependencies.html) that replaces all CDK dependencies with a Git dependency at the same commit. 25 | 26 | You can find the dependencies responsible with the command `cargo tree -i ic-cdk`. 27 | -------------------------------------------------------------------------------- /ic-cdk/tests/bitcoin_candid_equality.rs: -------------------------------------------------------------------------------- 1 | //! This test checks that the generated candid interface is equal to the one in the bitcoin.did file. 2 | //! 3 | //! The bitcoin.did file comes from https://github.com/dfinity/bitcoin-canister/blob/master/canister/candid.did 4 | //! 5 | //! Following items in bitcoin.did are commented out because they are not implemented in the bitcoin_canister module: 6 | //! - The init args `(init_config)`. 7 | //! - The `*_query` methods. Calling them from inter-canister calls would result in "<_query> cannot be called in replicated mode" rejection. 8 | //! - The `get_config` and `set_config` methods. 9 | 10 | #![allow(unused)] 11 | use candid::candid_method; 12 | use ic_cdk::bitcoin_canister::*; 13 | 14 | #[candid_method(update)] 15 | fn bitcoin_get_balance(_: GetBalanceRequest) -> Satoshi { 16 | unimplemented!() 17 | } 18 | 19 | #[candid_method(update)] 20 | fn bitcoin_get_utxos(_: GetUtxosRequest) -> GetUtxosResponse { 21 | unimplemented!() 22 | } 23 | 24 | #[candid_method(update)] 25 | fn bitcoin_get_current_fee_percentiles( 26 | _: GetCurrentFeePercentilesRequest, 27 | ) -> Vec { 28 | unimplemented!() 29 | } 30 | 31 | #[candid_method(update)] 32 | fn bitcoin_get_block_headers(_: GetBlockHeadersRequest) -> GetBlockHeadersResponse { 33 | unimplemented!() 34 | } 35 | 36 | #[candid_method(update)] 37 | fn bitcoin_send_transaction(_: SendTransactionRequest) { 38 | unimplemented!() 39 | } 40 | 41 | #[cfg(test)] 42 | mod test { 43 | use candid_parser::utils::{CandidSource, service_equal}; 44 | use ic_cdk::bitcoin_canister::*; 45 | 46 | #[test] 47 | fn candid_equality_test() { 48 | let declared_interface_str = 49 | std::fs::read_to_string("tests/bitcoin.did").expect("failed to read bitcoin.did file"); 50 | let declared_interface = CandidSource::Text(&declared_interface_str); 51 | 52 | candid::export_service!(); 53 | let implemented_interface_str = __export_service(); 54 | let implemented_interface = CandidSource::Text(&implemented_interface_str); 55 | 56 | let result = service_equal(declared_interface, implemented_interface); 57 | assert!(result.is_ok(), "{:?}", result.unwrap_err()); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /ic-cdk/src/api/management_canister/provisional.rs: -------------------------------------------------------------------------------- 1 | //! Provisional functions only available in local development instances. 2 | 3 | use crate::api::call::{CallResult, call}; 4 | use candid::{CandidType, Nat, Principal}; 5 | use serde::{Deserialize, Serialize}; 6 | 7 | pub use super::main::{CanisterId, CanisterIdRecord, CanisterSettings}; 8 | 9 | /// Argument type of [`provisional_create_canister_with_cycles`]. 10 | #[derive( 11 | CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, 12 | )] 13 | pub struct ProvisionalCreateCanisterWithCyclesArgument { 14 | /// The created canister will have this amount of cycles. 15 | pub amount: Option, 16 | /// See [`CanisterSettings`]. 17 | pub settings: Option, 18 | } 19 | 20 | /// Argument type of [`provisional_top_up_canister`]. 21 | #[derive( 22 | CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, 23 | )] 24 | pub struct ProvisionalTopUpCanisterArgument { 25 | /// Canister ID. 26 | pub canister_id: CanisterId, 27 | /// Amount of cycles to be added. 28 | pub amount: Nat, 29 | } 30 | 31 | /// Create a new canister with specified amount of cycles balance. 32 | /// 33 | /// This method is only available in local development instances. 34 | /// 35 | /// See [IC method `provisional_create_canister_with_cycles`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-provisional_create_canister_with_cycles). 36 | pub async fn provisional_create_canister_with_cycles( 37 | arg: ProvisionalCreateCanisterWithCyclesArgument, 38 | ) -> CallResult<(CanisterIdRecord,)> { 39 | call( 40 | Principal::management_canister(), 41 | "provisional_create_canister_with_cycles", 42 | (arg,), 43 | ) 44 | .await 45 | } 46 | 47 | /// Add cycles to a canister. 48 | /// 49 | /// This method is only available in local development instances. 50 | /// 51 | /// See [IC method `provisional_top_up_canister`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-provisional_top_up_canister). 52 | pub async fn provisional_top_up_canister(arg: ProvisionalTopUpCanisterArgument) -> CallResult<()> { 53 | call( 54 | Principal::management_canister(), 55 | "provisional_top_up_canister", 56 | (arg,), 57 | ) 58 | .await 59 | } 60 | -------------------------------------------------------------------------------- /ic-cdk/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc = include_str!("../README.md")] 2 | #![warn( 3 | elided_lifetimes_in_paths, 4 | missing_debug_implementations, 5 | missing_docs, 6 | unsafe_op_in_unsafe_fn, 7 | clippy::undocumented_unsafe_blocks, 8 | clippy::missing_safety_doc 9 | )] 10 | #![cfg_attr(docsrs, feature(doc_cfg))] 11 | 12 | #[cfg(target_feature = "atomics")] 13 | compile_error!("This version of the CDK does not support multithreading."); 14 | 15 | pub mod api; 16 | pub mod bitcoin_canister; 17 | pub mod call; 18 | pub mod futures; 19 | mod macros; 20 | pub mod management_canister; 21 | pub mod stable; 22 | pub mod storage; 23 | 24 | use std::future::Future; 25 | 26 | #[doc(inline)] 27 | pub use api::trap; 28 | 29 | #[doc(hidden)] 30 | #[allow(deprecated)] 31 | pub use api::{ 32 | call::{call, notify}, 33 | caller, id, print, 34 | }; 35 | 36 | #[doc(inline)] 37 | pub use macros::*; 38 | 39 | /// Format and then print the formatted message 40 | #[cfg(target_family = "wasm")] 41 | #[macro_export] 42 | macro_rules! println { 43 | ($fmt:expr) => ($crate::api::debug_print(format!($fmt))); 44 | ($fmt:expr, $($arg:tt)*) => ($crate::api::debug_print(format!($fmt, $($arg)*))); 45 | } 46 | 47 | /// Format and then print the formatted message 48 | #[cfg(not(target_family = "wasm"))] 49 | #[macro_export] 50 | macro_rules! println { 51 | ($fmt:expr) => (std::println!($fmt)); 52 | ($fmt:expr, $($arg:tt)*) => (std::println!($fmt, $($arg)*)); 53 | } 54 | 55 | /// Format and then print the formatted message 56 | #[cfg(target_family = "wasm")] 57 | #[macro_export] 58 | macro_rules! eprintln { 59 | ($fmt:expr) => ($crate::api::debug_print(format!($fmt))); 60 | ($fmt:expr, $($arg:tt)*) => ($crate::api::debug_print(format!($fmt, $($arg)*))); 61 | } 62 | 63 | /// Format and then print the formatted message 64 | #[cfg(not(target_family = "wasm"))] 65 | #[macro_export] 66 | macro_rules! eprintln { 67 | ($fmt:expr) => (std::eprintln!($fmt)); 68 | ($fmt:expr, $($arg:tt)*) => (std::eprintln!($fmt, $($arg)*)); 69 | } 70 | 71 | #[doc(hidden)] 72 | #[deprecated( 73 | since = "0.18.0", 74 | note = "Use ic_cdk::futures::spawn_017_compat. Alternatively, migrate to ic_cdk::futures::spawn; 75 | code execution order will change, see https://github.com/dfinity/cdk-rs/blob/0.18.3/ic-cdk/V18_GUIDE.md#futures-ordering-changes" 76 | )] 77 | pub fn spawn>(fut: F) { 78 | crate::futures::spawn_017_compat(fut); 79 | } 80 | -------------------------------------------------------------------------------- /e2e-tests/tests/http_request.rs: -------------------------------------------------------------------------------- 1 | use candid::{Encode, Principal}; 2 | use pocket_ic::common::rest::{ 3 | CanisterHttpHeader, CanisterHttpReply, CanisterHttpRequest, CanisterHttpResponse, 4 | MockCanisterHttpResponse, 5 | }; 6 | use pocket_ic::PocketIc; 7 | 8 | mod test_utilities; 9 | use test_utilities::{cargo_build_canister, pic_base}; 10 | 11 | #[test] 12 | fn test_http_request() { 13 | let wasm = cargo_build_canister("http_request"); 14 | let pic = pic_base().build(); 15 | 16 | let canister_id = pic.create_canister(); 17 | pic.add_cycles(canister_id, 3_000_000_000_000u128); 18 | pic.install_canister(canister_id, wasm, vec![], None); 19 | 20 | test_one_http_request(&pic, canister_id, "get_without_transform"); 21 | test_one_http_request(&pic, canister_id, "post"); 22 | test_one_http_request(&pic, canister_id, "head"); 23 | test_one_http_request(&pic, canister_id, "get_with_transform"); 24 | test_one_http_request(&pic, canister_id, "get_with_transform_closure"); 25 | test_one_http_request(&pic, canister_id, "non_replicated"); 26 | } 27 | 28 | fn test_one_http_request(pic: &PocketIc, canister_id: Principal, method: &str) { 29 | let call_id = pic 30 | .submit_call( 31 | canister_id, 32 | Principal::anonymous(), 33 | method, 34 | Encode!(&()).unwrap(), 35 | ) 36 | .unwrap(); 37 | let canister_http_requests = tick_until_next_request(pic); 38 | assert_eq!(canister_http_requests.len(), 1); 39 | let request = &canister_http_requests[0]; 40 | pic.mock_canister_http_response(MockCanisterHttpResponse { 41 | subnet_id: request.subnet_id, 42 | request_id: request.request_id, 43 | response: CanisterHttpResponse::CanisterHttpReply(CanisterHttpReply { 44 | status: 200, 45 | headers: vec![CanisterHttpHeader { 46 | name: "response_header_name".to_string(), 47 | value: "response_header_value".to_string(), 48 | }], 49 | body: vec![42], 50 | }), 51 | additional_responses: vec![], 52 | }); 53 | pic.await_call(call_id).unwrap(); 54 | } 55 | 56 | fn tick_until_next_request(pic: &PocketIc) -> Vec { 57 | for _ in 0..10 { 58 | let requests = pic.get_canister_http(); 59 | if !requests.is_empty() { 60 | return requests; 61 | } 62 | pic.tick(); 63 | } 64 | vec![] 65 | } 66 | -------------------------------------------------------------------------------- /ic-cdk-timers/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [unreleased] 8 | 9 | ## [1.0.0] - 2025-11-13 10 | 11 | - `ic-cdk-timers` no longer has a dependency on `ic-cdk` and no longer needs to be upgraded when `ic-cdk` is upgraded. 12 | - Breaking: Timer body function signatures have been updated which eliminating the need for explicit `spawn` calls within timer callbacks. 13 | - `set_timer`: now takes `impl Future`. `|| {}` should be changed to `async {}`, 14 | - `set_timer_interval`: now takes `FnMut() -> impl Future`. `|| {}` should be changed to `|| async {}`. 15 | - `set_timer_interval_serial`: new function, takes `AsyncFnMut()`. 16 | - If you have any immediate `spawn` calls, you can remove them and run the async code directly. (You do not have to.) 17 | 18 | ## [0.12.2] - 2025-06-25 19 | 20 | - Upgrade `ic0` to v1.0. 21 | 22 | ## [0.12.1] - 2025-06-17 23 | 24 | - Upgrade `ic0` to v0.25. 25 | 26 | ## [0.12.0] - 2025-04-22 27 | 28 | - Upgrade `ic-cdk` to v0.18. 29 | 30 | ## [0.11.0] - 2024-11-04 31 | 32 | ### Changed 33 | 34 | - Upgrade `ic-cdk` to v0.17. 35 | 36 | ## [0.10.0] - 2024-08-27 37 | 38 | ### Changed 39 | 40 | - Upgrade `ic-cdk` to v0.16. 41 | 42 | ## [0.9.0] - 2024-07-01 43 | 44 | ### Changed 45 | 46 | - Upgrade `ic-cdk` to v0.15. 47 | 48 | ## [0.8.0] - 2024-05-17 49 | 50 | ### Changed 51 | 52 | - Upgrade `ic-cdk` to v0.14. 53 | 54 | ## [0.7.0] - 2024-03-01 55 | 56 | ### Changed 57 | 58 | - Upgrade `ic-cdk` to v0.13. 59 | 60 | ## [0.6.0] - 2023-11-23 61 | 62 | ### Changed 63 | 64 | - Upgrade `ic-cdk` to v0.12. 65 | 66 | ## [0.5.0] - 2023-09-18 67 | 68 | ### Changed 69 | 70 | - Upgrade `ic-cdk` to v0.11. 71 | 72 | ## [0.4.0] - 2023-07-13 73 | 74 | ### Changed 75 | 76 | - Upgrade `ic-cdk` to v0.10. 77 | 78 | ## [0.3.0] - 2023-06-20 79 | 80 | ### Changed 81 | 82 | - Upgrade `ic-cdk` to v0.9. 83 | 84 | ## [0.2.0] - 2023-05-26 85 | 86 | ### Changed 87 | 88 | - Upgrade `ic-cdk` to v0.8. 89 | 90 | ## [0.1.2] - 2023-03-01 91 | 92 | ## [0.1.1] - 2023-02-22 93 | 94 | ### Fixed 95 | 96 | - Broken references to `ic_cdk::api::time`. 97 | 98 | ## [0.1.0] - 2023-02-03 99 | 100 | ### Added 101 | 102 | - Initial release of the `ic-cdk-timers` library. 103 | -------------------------------------------------------------------------------- /e2e-tests/tests/chunk.rs: -------------------------------------------------------------------------------- 1 | use candid::Principal; 2 | use sha2::Digest; 3 | 4 | mod test_utilities; 5 | use test_utilities::{cargo_build_canister, pic_base, update}; 6 | 7 | #[test] 8 | fn test_chunk() { 9 | let wasm = cargo_build_canister("chunk"); 10 | let pic = pic_base().build(); 11 | let canister_id = pic.create_canister(); 12 | pic.add_cycles(canister_id, 100_000_000_000_000); 13 | pic.install_canister(canister_id, wasm, vec![], None); 14 | let (target_canister_id,): (Principal,) = 15 | update(&pic, canister_id, "call_create_canister", ()).unwrap(); 16 | 17 | let wasm_module = b"\x00asm\x01\x00\x00\x00".to_vec(); 18 | let wasm_module_hash = sha2::Sha256::digest(&wasm_module).to_vec(); 19 | let chunk1 = wasm_module[..4].to_vec(); 20 | let chunk2 = wasm_module[4..].to_vec(); 21 | let hash1_expected = sha2::Sha256::digest(&chunk1).to_vec(); 22 | let hash2_expected = sha2::Sha256::digest(&chunk2).to_vec(); 23 | 24 | let (hash1_return,): (Vec,) = update( 25 | &pic, 26 | canister_id, 27 | "call_upload_chunk", 28 | (target_canister_id, chunk1.clone()), 29 | ) 30 | .unwrap(); 31 | assert_eq!(&hash1_return, &hash1_expected); 32 | 33 | let () = update( 34 | &pic, 35 | canister_id, 36 | "call_clear_chunk_store", 37 | (target_canister_id,), 38 | ) 39 | .unwrap(); 40 | 41 | let (_hash1_return,): (Vec,) = update( 42 | &pic, 43 | canister_id, 44 | "call_upload_chunk", 45 | (target_canister_id, chunk1), 46 | ) 47 | .unwrap(); 48 | let (_hash2_return,): (Vec,) = update( 49 | &pic, 50 | canister_id, 51 | "call_upload_chunk", 52 | (target_canister_id, chunk2), 53 | ) 54 | .unwrap(); 55 | 56 | let (hashes,): (Vec>,) = update( 57 | &pic, 58 | canister_id, 59 | "call_stored_chunks", 60 | (target_canister_id,), 61 | ) 62 | .unwrap(); 63 | // the hashes returned are not guaranteed to be in order 64 | assert_eq!(hashes.len(), 2); 65 | assert!(hashes.contains(&hash1_expected)); 66 | assert!(hashes.contains(&hash2_expected)); 67 | 68 | let () = update( 69 | &pic, 70 | canister_id, 71 | "call_install_chunked_code", 72 | ( 73 | target_canister_id, 74 | // the order of the hashes matters 75 | vec![hash1_expected, hash2_expected], 76 | wasm_module_hash, 77 | ), 78 | ) 79 | .unwrap(); 80 | } 81 | -------------------------------------------------------------------------------- /legacy/ic-cdk-optimizer/src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::Parser; 2 | use humansize::{file_size_opts, FileSize}; 3 | use std::io::{Read, Write}; 4 | use std::path::{Path, PathBuf}; 5 | 6 | mod passes; 7 | 8 | #[derive(Parser, Debug)] 9 | #[clap(version)] 10 | struct CommandLineOpts { 11 | /// Input file to optimize. By default, or if "-", will use STDIN. 12 | #[clap(default_value("-"))] 13 | input: PathBuf, 14 | 15 | /// Output file. If unset, the original file will be overwritten. If "-", or if unset and the original was passed via STDIN, the result will go to STDOUT. 16 | #[clap(short, long)] 17 | output: Option, 18 | } 19 | 20 | fn main() { 21 | let passes = passes::create(); 22 | let opts = CommandLineOpts::parse(); 23 | let content = if opts.input != Path::new("-") { 24 | std::fs::read(&opts.input).expect("Could not read the file.") 25 | } else { 26 | let mut buff = Vec::new(); 27 | std::io::stdin() 28 | .read_to_end(&mut buff) 29 | .expect("Could not read STDIN."); 30 | buff 31 | }; 32 | 33 | eprintln!( 34 | "Original: {:>8}", 35 | content.len().file_size(file_size_opts::BINARY).unwrap() 36 | ); 37 | 38 | let original_wasm_size = content.len(); 39 | let mut wasm_size = content.len(); 40 | let mut wasm_back = content; 41 | 42 | for pass in passes { 43 | eprintln!("{}...", pass.description()); 44 | let new_wasm = pass.opt(&wasm_back).expect("Pass failed:"); 45 | if new_wasm.len() < wasm_back.len() { 46 | wasm_back = new_wasm; 47 | eprintln!( 48 | " Size: {:>8} ({:3.1}% smaller)", 49 | wasm_back.len().file_size(file_size_opts::BINARY).unwrap(), 50 | (1.0 - ((wasm_back.len() as f64) / (wasm_size as f64))) * 100.0 51 | ); 52 | } else { 53 | eprintln!("Pass did not result in smaller WASM... Skipping."); 54 | } 55 | wasm_size = wasm_back.len(); 56 | } 57 | 58 | eprintln!( 59 | "\nFinal Size: {} ({:3.1}% smaller)", 60 | wasm_back.len().file_size(file_size_opts::BINARY).unwrap(), 61 | (1.0 - ((wasm_back.len() as f64) / (original_wasm_size as f64))) * 100.0 62 | ); 63 | let outfile = opts.output.unwrap_or(opts.input); 64 | if outfile == Path::new("-") { 65 | std::io::stdout() 66 | .write_all(&wasm_back) 67 | .expect("Could not write output."); 68 | } else { 69 | std::fs::write(outfile, wasm_back).expect("Could not write output file."); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /ic-cdk/src/api/management_canister/ecdsa/types.rs: -------------------------------------------------------------------------------- 1 | use candid::CandidType; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use super::super::main::CanisterId; 5 | 6 | /// Argument type of [`ecdsa_public_key`](super::ecdsa_public_key). 7 | #[derive( 8 | CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, 9 | )] 10 | pub struct EcdsaPublicKeyArgument { 11 | /// Canister id, default to the canister id of the caller if None. 12 | pub canister_id: Option, 13 | /// A vector of variable length byte strings. 14 | pub derivation_path: Vec>, 15 | /// See [`EcdsaKeyId`]. 16 | pub key_id: EcdsaKeyId, 17 | } 18 | 19 | /// Response Type of [`ecdsa_public_key`](super::ecdsa_public_key). 20 | #[derive( 21 | CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, 22 | )] 23 | pub struct EcdsaPublicKeyResponse { 24 | /// An ECDSA public key encoded in SEC1 compressed form. 25 | pub public_key: Vec, 26 | /// Can be used to deterministically derive child keys of the `public_key`. 27 | pub chain_code: Vec, 28 | } 29 | 30 | /// Argument type of [`sign_with_ecdsa`](super::sign_with_ecdsa). 31 | #[derive( 32 | CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, 33 | )] 34 | pub struct SignWithEcdsaArgument { 35 | /// Hash of the message with length of 32 bytes. 36 | pub message_hash: Vec, 37 | /// A vector of variable length byte strings. 38 | pub derivation_path: Vec>, 39 | /// See [`EcdsaKeyId`]. 40 | pub key_id: EcdsaKeyId, 41 | } 42 | 43 | /// Response type of [`sign_with_ecdsa`](super::sign_with_ecdsa). 44 | #[derive( 45 | CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, 46 | )] 47 | pub struct SignWithEcdsaResponse { 48 | /// Encoded as the concatenation of the SEC1 encodings of the two values r and s. 49 | pub signature: Vec, 50 | } 51 | 52 | /// ECDSA key ID. 53 | #[derive( 54 | CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, 55 | )] 56 | pub struct EcdsaKeyId { 57 | /// See [`EcdsaCurve`]. 58 | pub curve: EcdsaCurve, 59 | /// Name. 60 | pub name: String, 61 | } 62 | 63 | /// ECDSA Curve. 64 | #[derive( 65 | CandidType, 66 | Serialize, 67 | Deserialize, 68 | Debug, 69 | PartialEq, 70 | Eq, 71 | PartialOrd, 72 | Ord, 73 | Hash, 74 | Clone, 75 | Copy, 76 | Default, 77 | )] 78 | pub enum EcdsaCurve { 79 | /// secp256k1 80 | #[serde(rename = "secp256k1")] 81 | #[default] 82 | Secp256k1, 83 | } 84 | -------------------------------------------------------------------------------- /e2e-tests/src/bin/canister_info.rs: -------------------------------------------------------------------------------- 1 | use candid::Principal; 2 | use ic_cdk::management_canister::{ 3 | canister_info, create_canister_with_extra_cycles, install_code, uninstall_code, 4 | update_settings, CanisterInfoArgs, CanisterInfoResult, 5 | CanisterInstallMode::{Install, Reinstall, Upgrade}, 6 | CanisterSettings, CreateCanisterArgs, InstallCodeArgs, UninstallCodeArgs, UpdateSettingsArgs, 7 | }; 8 | 9 | #[ic_cdk::update] 10 | async fn info(canister_id: Principal) -> CanisterInfoResult { 11 | let request = CanisterInfoArgs { 12 | canister_id, 13 | num_requested_changes: Some(20), 14 | }; 15 | canister_info(&request).await.unwrap() 16 | } 17 | 18 | #[ic_cdk::update] 19 | async fn canister_lifecycle() -> Principal { 20 | let canister_id = 21 | create_canister_with_extra_cycles(&CreateCanisterArgs::default(), 1_000_000_000_000) 22 | .await 23 | .unwrap() 24 | .canister_id; 25 | install_code(&InstallCodeArgs { 26 | mode: Install, 27 | arg: vec![], 28 | wasm_module: vec![0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00], 29 | canister_id, 30 | }) 31 | .await 32 | .unwrap(); 33 | uninstall_code(&UninstallCodeArgs { canister_id }) 34 | .await 35 | .unwrap(); 36 | install_code(&InstallCodeArgs { 37 | mode: Install, 38 | arg: vec![], 39 | wasm_module: vec![0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00], 40 | canister_id, 41 | }) 42 | .await 43 | .unwrap(); 44 | install_code(&InstallCodeArgs { 45 | mode: Reinstall, 46 | arg: vec![], 47 | wasm_module: vec![0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00], 48 | canister_id, 49 | }) 50 | .await 51 | .unwrap(); 52 | install_code(&InstallCodeArgs { 53 | mode: Upgrade(None), 54 | arg: vec![], 55 | wasm_module: vec![0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00], 56 | canister_id, 57 | }) 58 | .await 59 | .unwrap(); 60 | update_settings(&UpdateSettingsArgs { 61 | settings: CanisterSettings { 62 | controllers: Some(vec![ 63 | ic_cdk::api::canister_self(), 64 | canister_id, 65 | Principal::anonymous(), 66 | ]), 67 | compute_allocation: None, 68 | memory_allocation: None, 69 | freezing_threshold: None, 70 | reserved_cycles_limit: None, 71 | log_visibility: None, 72 | wasm_memory_limit: None, 73 | wasm_memory_threshold: None, 74 | environment_variables: None, 75 | }, 76 | canister_id, 77 | }) 78 | .await 79 | .unwrap(); 80 | canister_id 81 | } 82 | 83 | fn main() {} 84 | -------------------------------------------------------------------------------- /ic-cdk/src/api/management_canister/schnorr/types.rs: -------------------------------------------------------------------------------- 1 | use candid::CandidType; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use super::super::main::CanisterId; 5 | 6 | /// Argument Type of [`schnorr_public_key`](super::schnorr_public_key). 7 | #[derive( 8 | CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, 9 | )] 10 | pub struct SchnorrPublicKeyArgument { 11 | /// Canister id, default to the canister id of the caller if None. 12 | pub canister_id: Option, 13 | /// A vector of variable length byte strings. 14 | pub derivation_path: Vec>, 15 | /// See [`SchnorrKeyId`]. 16 | pub key_id: SchnorrKeyId, 17 | } 18 | 19 | /// Response Type of [`schnorr_public_key`](super::schnorr_public_key). 20 | #[derive( 21 | CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, 22 | )] 23 | pub struct SchnorrPublicKeyResponse { 24 | /// An Schnorr public key encoded in SEC1 compressed form. 25 | pub public_key: Vec, 26 | /// Can be used to deterministically derive child keys of the `public_key`. 27 | pub chain_code: Vec, 28 | } 29 | 30 | /// Argument Type of [`sign_with_schnorr`](super::sign_with_schnorr). 31 | #[derive( 32 | CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, 33 | )] 34 | pub struct SignWithSchnorrArgument { 35 | /// Message to be signed. 36 | pub message: Vec, 37 | /// A vector of variable length byte strings. 38 | pub derivation_path: Vec>, 39 | /// See [`SchnorrKeyId`]. 40 | pub key_id: SchnorrKeyId, 41 | } 42 | 43 | /// Response Type of [`sign_with_schnorr`](super::sign_with_schnorr). 44 | #[derive( 45 | CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, 46 | )] 47 | pub struct SignWithSchnorrResponse { 48 | /// The encoding of the signature depends on the key ID's algorithm. 49 | pub signature: Vec, 50 | } 51 | 52 | /// Schnorr key ID. 53 | #[derive( 54 | CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, 55 | )] 56 | pub struct SchnorrKeyId { 57 | /// See [`SchnorrAlgorithm`]. 58 | pub algorithm: SchnorrAlgorithm, 59 | /// Name. 60 | pub name: String, 61 | } 62 | 63 | /// Schnorr Algorithm. 64 | #[derive( 65 | CandidType, 66 | Serialize, 67 | Deserialize, 68 | Debug, 69 | PartialEq, 70 | Eq, 71 | PartialOrd, 72 | Ord, 73 | Hash, 74 | Clone, 75 | Copy, 76 | Default, 77 | )] 78 | pub enum SchnorrAlgorithm { 79 | /// BIP-340 secp256k1. 80 | #[serde(rename = "bip340secp256k1")] 81 | #[default] 82 | Bip340secp256k1, 83 | /// ed25519. 84 | #[serde(rename = "ed25519")] 85 | Ed25519, 86 | } 87 | -------------------------------------------------------------------------------- /library/ic-ledger-types/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [unreleased] 8 | 9 | ## [0.16.0] - 2025-11-12 10 | 11 | ### Changed 12 | 13 | - Upgrade `ic-cdk` to v0.19. 14 | 15 | ## [0.15.0] - 2025-04-22 16 | 17 | ### Changed 18 | 19 | - Upgrade `ic-cdk` to v0.18. 20 | 21 | ## [0.14.0] - 2024-11-08 22 | 23 | ### Changed 24 | 25 | - Upgrade `ic-cdk` to v0.17. 26 | 27 | ### Added 28 | - as_bytes method to AccountIdentifier in ic-ledger-types 29 | 30 | ## [0.13.0] - 2024-08-27 31 | 32 | ### Changed 33 | 34 | - Upgrade `ic-cdk` to v0.16. 35 | 36 | ## [0.12.0] - 2024-07-01 37 | 38 | ### Changed 39 | 40 | - Upgrade `ic-cdk` to v0.15. 41 | 42 | ## [0.11.0] - 2024-05-17 43 | 44 | ### Changed 45 | 46 | - Upgrade `ic-cdk` to v0.14. 47 | 48 | ## [0.10.0] - 2024-03-01 49 | 50 | ### Changed 51 | - Upgrade `ic-cdk` to v0.13. 52 | 53 | ## [0.9.0] - 2023-11-23 54 | 55 | ### Changed 56 | - Upgrade `ic-cdk` to v0.12 and `candid` to v0.10. 57 | 58 | ## [0.8.0] - 2023-09-18 59 | 60 | ### Changed 61 | - Upgrade `ic-cdk` to v0.11. 62 | 63 | ## [0.7.0] - 2023-07-13 64 | 65 | ### Added 66 | - from_hex/from_slice/to_hex methods to AccountIdentifier in ic-ledger-types 67 | 68 | ### Changed 69 | - Upgrade `ic-cdk` to v0.10 and `candid` to v0.9. 70 | 71 | ## [0.6.0] - 2023-06-20 72 | ### Changed 73 | - Upgrade `ic-cdk` to v0.9. 74 | 75 | ## [0.5.0] - 2023-05-26 76 | ### Changed 77 | - Upgrade `ic-cdk` to v0.8. 78 | 79 | ## [0.4.2] - 2023-03-01 80 | ### Fixed 81 | - Fill missing docs. 82 | 83 | ## [0.4.1] - 2023-02-22 84 | ### Fixed 85 | - Use automatic link in document. 86 | 87 | ## [0.4.0] - 2023-02-13 88 | ### Changed 89 | - Extend the Operation type to support approve/transfer_from transactions. 90 | 91 | ## [0.3.0] - 2023-02-03 92 | ### Changed 93 | - Upgrade `ic-cdk` to v0.7. 94 | 95 | ## [0.2.1] - 2023-01-20 96 | 97 | ### Added 98 | 99 | - Implemented `From` for `Subaccount` (#361) 100 | 101 | ## [0.2.0] - 2022-11-04 102 | ### Changed 103 | - Upgrade `ic-cdk` to v0.6 and `candid` to v0.8. 104 | 105 | ## [0.1.2] - 2022-05-31 106 | ### Added 107 | - Integrate with the ledger's `token_symbol` method 108 | - Methods to query ledger blocks. 109 | 110 | ### Changed 111 | - Support conversion from `[u8; 32]` to `AccountIdentifier` via `TryFrom` with CRC-32 check. 112 | - Upgrade `ic-cdk` to 0.5.0 113 | 114 | ## [0.1.1] - 2022-02-04 115 | ### Changed 116 | - Upgrade `ic-cdk` to v0.4.0. 117 | 118 | ## [0.1.0] - 2021-11-11 119 | ### Added 120 | - Initial release of the library. 121 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust Canister Development Kit 2 | 3 | [![Documentation](https://docs.rs/ic-cdk/badge.svg)](https://docs.rs/ic-cdk/) 4 | [![Crates.io](https://img.shields.io/crates/v/ic-cdk.svg)](https://crates.io/crates/ic-cdk) 5 | [![License](https://img.shields.io/crates/l/ic-cdk.svg)](https://github.com/dfinity/cdk-rs/blob/main/LICENSE) 6 | [![Downloads](https://img.shields.io/crates/d/ic-cdk.svg)](https://crates.io/crates/ic-cdk) 7 | [![CI](https://github.com/dfinity/cdk-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/dfinity/cdk-rs/actions/workflows/ci.yml) 8 | 9 | **Rust CDK provides tools for building Canisters on Internet Computer (IC).** 10 | 11 | You may be looking for: 12 | 13 | - [Documentation Site of the Internet Computer](https://internetcomputer.org/docs) 14 | - [Tutorials of Rust CDK](https://internetcomputer.org/docs/current/developer-docs/backend/rust/) 15 | - [`dfx` for managing IC projects](https://github.com/dfinity/sdk) 16 | 17 | If you are looking for a crate to communicate with existing canisters on IC, 18 | you may want to check [agent-rs](https://github.com/dfinity/agent-rs). 19 | 20 | # Introduction 21 | 22 | A `canister` is a WebAssembly (wasm) module that can run on the Internet Computer. 23 | 24 | To be a `canister`, a wasm module should communicate with the execution environment using [Canister interfaces (System API)](https://internetcomputer.org/docs/current/references/ic-interface-spec/#system-api). 25 | 26 | This repo provides libraries and tools to facilitate developing canisters in Rust. 27 | 28 | - [`ic0`](ic0): 29 | Internet Computer System API binding. 30 | - [`ic-cdk`](ic-cdk): 31 | Internet Computer Canister Development Kit. 32 | - [`ic-cdk-bindgen`](ic-cdk-bindgen): 33 | Generate Rust bindings from Candid to make inter-canister calls. 34 | - [`ic-cdk-macros`](ic-cdk-macros): 35 | Annotate functions with attribute macros to make them exposed public interfaces. 36 | - [`ic-cdk-timers`](ic-cdk-timers): 37 | The library implements multiple and periodic timers. 38 | - [`ic-management-canister-types`](ic-management-canister-types): Types for calling the IC management canister. 39 | - [`ic-certified-map`](library/ic-certified-map): 40 | An implementation of map which support *certified queries*. 41 | - [`ic-ledger-types`](library/ic-ledger-types): 42 | Type definitions to communicate with the ICP ledger canister. 43 | 44 | ## Rust CDK in Action 45 | 46 | In Cargo.toml: 47 | 48 | ```toml 49 | [lib] 50 | crate-type = ["cdylib"] 51 | 52 | [dependencies] 53 | ic-cdk = "0.18" 54 | candid = "0.10" # required if you want to define Candid data types 55 | ``` 56 | 57 | Then in Rust source code: 58 | 59 | ```rust 60 | #[ic_cdk::query] 61 | fn hello() -> String{ 62 | "world".to_string() 63 | } 64 | ``` 65 | 66 | Check [Rust quickstart](https://internetcomputer.org/docs/current/developer-docs/backend/rust/quickstart) for a detailed guidance. 67 | -------------------------------------------------------------------------------- /ic-cdk-executor/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! An async executor for [`ic-cdk`](https://docs.rs/ic-cdk). Most users should not use this crate directly. It is useful 2 | //! primarily for those who are writing their own CDK or a runtime host for non-Rust languages. 3 | //! 4 | //! ## Contexts 5 | //! 6 | //! The expected boilerplate for a canister method or other entrypoint (*not* including callbacks) looks like this: 7 | //! 8 | //! ``` 9 | //! # use ic_cdk_executor::*; 10 | //! pub extern "C" fn function() { 11 | //! in_tracking_executor_context(|| { 12 | //! // method goes here 13 | //! }); 14 | //! } 15 | //! ``` 16 | //! 17 | //! The [`in_tracking_executor_context`] function permits you to call `spawn_*` functions. As little code as possible 18 | //! should exist outside the block, because [`in_tracking_executor_context`] additionally sets up the panic handler. 19 | //! 20 | //! The above applies to update contexts. Query contexts, including `canister_inspect_message`, should use 21 | //! [`in_tracking_query_executor_context`]. 22 | //! 23 | //! The expected boilerplate for an inter-canister call callback looks like this: 24 | //! 25 | //! ``` 26 | //! # use ic_cdk_executor::*; 27 | //! # fn unpack_env(env: usize) -> MethodHandle { unimplemented!() } 28 | //! unsafe extern "C" fn callback(env: usize) { 29 | //! let method = unpack_env(env); 30 | //! in_callback_executor_context_for(method, || { 31 | //! // wake the call future 32 | //! }); 33 | //! } 34 | //! unsafe extern "C" fn cleanup(env: usize) { 35 | //! let method = unpack_env(env); 36 | //! in_trap_recovery_context_for(method, || { 37 | //! cancel_all_tasks_attached_to_current_method(); 38 | //! }); 39 | //! } 40 | //! ``` 41 | //! 42 | //! In async contexts, all scheduled tasks are run *after* the closure passed to the context function 43 | //! returns, but *before* the context function itself returns. 44 | //! 45 | //! The `method` parameter must be retrieved *before* making inter-canister calls via the [`extend_current_method_context`] 46 | //! function. Calling this function from the callback instead will trap. 47 | //! 48 | //! ## Protection 49 | //! 50 | //! Tasks can be either *protected* or *migratory*. Protected tasks are attached to the method that spawned them, 51 | //! when awoken will not resume until that method continues, and will be canceled if the method returns before they complete. 52 | //! Migratory tasks are not attached to any method, and will resume in whatever method wakes them. 53 | mod machinery; 54 | 55 | #[doc(inline)] 56 | pub use machinery::{ 57 | MethodHandle, TaskHandle, cancel_all_tasks_attached_to_current_method, cancel_task, 58 | extend_current_method_context, in_callback_executor_context_for, in_tracking_executor_context, 59 | in_tracking_query_executor_context, in_trap_recovery_context_for, is_recovering_from_trap, 60 | spawn_migratory, spawn_protected, 61 | }; 62 | -------------------------------------------------------------------------------- /library/ic-certified-map/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This package provides a map backed by a Merkle tree that can be used 2 | //! by Internet Computer canisters to implement certified queries. 3 | //! 4 | //! You can certify your data by using the [`RbTree`] type as a map of 5 | //! known names to the values you want certified. After you record its 6 | //! [`root_hash`](RbTree::root_hash) into your canister's [certified data], 7 | //! query calls can access a [data certificate] proving that the IC certified 8 | //! the hash, under the [path] `/canister//certified_data`. 9 | //! By providing this certificate, as well as a [`witness`](RbTree::witness) 10 | //! that the value exists in the hash, you can then prove to the caller that 11 | //! the IC certified the data. 12 | //! 13 | //! [certified data]: https://docs.rs/ic-cdk/latest/ic_cdk/api/fn.set_certified_data.html 14 | //! [data certificate]: https://docs.rs/ic-cdk/latest/ic_cdk/api/fn.data_certificate.html 15 | //! [path]: https://internetcomputer.org/docs/current/references/ic-interface-spec#state-tree 16 | //! 17 | //! # Example 18 | //! 19 | //! ``` 20 | //! # use std::cell::*; 21 | //! # use ic_cdk::*; 22 | //! # use ic_certified_map::*; 23 | //! # use candid::CandidType; 24 | //! # use serde::Serialize; 25 | //! 26 | //! thread_local! { 27 | //! static COUNTER: Cell = Cell::new(0); 28 | //! static TREE: RefCell> = RefCell::new(RbTree::new()); 29 | //! } 30 | //! 31 | //! #[update] 32 | //! fn inc() { 33 | //! let count = COUNTER.with(|counter| { 34 | //! let count = counter.get() + 1; 35 | //! counter.set(count); 36 | //! count 37 | //! }); 38 | //! TREE.with(|tree| { 39 | //! let mut tree = tree.borrow_mut(); 40 | //! tree.insert("counter", leaf_hash(&count.to_be_bytes())); 41 | //! ic_cdk::api::set_certified_data(&tree.root_hash()); 42 | //! }) 43 | //! } 44 | //! 45 | //! #[derive(CandidType)] 46 | //! struct CertifiedCounter { 47 | //! count: i32, 48 | //! certificate: Vec, 49 | //! witness: Vec, 50 | //! } 51 | //! 52 | //! #[query] 53 | //! fn get() -> CertifiedCounter { 54 | //! let certificate = ic_cdk::api::data_certificate().expect("No data certificate available"); 55 | //! let witness = TREE.with(|tree| { 56 | //! let tree = tree.borrow(); 57 | //! let mut witness = vec![]; 58 | //! let mut witness_serializer = serde_cbor::Serializer::new(&mut witness); 59 | //! witness_serializer.self_describe(); 60 | //! tree.witness(b"counter").serialize(&mut witness_serializer).unwrap(); 61 | //! witness 62 | //! }); 63 | //! let count = COUNTER.with(|counter| counter.get()); 64 | //! CertifiedCounter { 65 | //! count, 66 | //! certificate, 67 | //! witness, 68 | //! } 69 | //! } 70 | //! ``` 71 | 72 | #![warn( 73 | elided_lifetimes_in_paths, 74 | missing_debug_implementations, 75 | missing_docs, 76 | unsafe_op_in_unsafe_fn, 77 | clippy::undocumented_unsafe_blocks, 78 | clippy::missing_safety_doc 79 | )] 80 | 81 | mod hashtree; 82 | mod rbtree; 83 | 84 | pub use crate::hashtree::*; 85 | pub use crate::rbtree::*; 86 | -------------------------------------------------------------------------------- /ic-cdk-macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc = include_str!("../README.md")] 2 | #![warn( 3 | elided_lifetimes_in_paths, 4 | missing_debug_implementations, 5 | unsafe_op_in_unsafe_fn, 6 | clippy::undocumented_unsafe_blocks, 7 | clippy::missing_safety_doc 8 | )] 9 | 10 | use proc_macro::TokenStream; 11 | use syn::Error; 12 | 13 | mod export; 14 | 15 | fn handle_debug_and_errors( 16 | cb: F, 17 | name: &str, 18 | attr: TokenStream, 19 | item: TokenStream, 20 | ) -> TokenStream 21 | where 22 | F: FnOnce( 23 | proc_macro2::TokenStream, 24 | proc_macro2::TokenStream, 25 | ) -> Result, 26 | { 27 | if std::env::var_os("IC_CDK_DEBUG").is_some() { 28 | eprintln!("--- IC_CDK_MACROS DEBUG ---"); 29 | eprintln!("{name}\n attr: {attr}\n item: {item}"); 30 | } 31 | 32 | let result = cb(attr.into(), item.into()); 33 | 34 | if std::env::var_os("IC_CDK_DEBUG").is_some() { 35 | eprintln!("--------- RESULT ---------"); 36 | if let Ok(ref stream) = result { 37 | eprintln!("{stream}"); 38 | } 39 | eprintln!("---------------------------"); 40 | } 41 | 42 | result.map_or_else(|e| e.to_compile_error().into(), Into::into) 43 | } 44 | 45 | #[proc_macro] 46 | pub fn export_candid(input: TokenStream) -> TokenStream { 47 | let input: proc_macro2::TokenStream = input.into(); 48 | quote::quote! { 49 | ::candid::export_service!(#input); 50 | 51 | #[unsafe(no_mangle)] 52 | pub fn get_candid_pointer() -> *mut std::os::raw::c_char { 53 | let c_string = std::ffi::CString::new(__export_service()).unwrap(); 54 | c_string.into_raw() 55 | } 56 | } 57 | .into() 58 | } 59 | 60 | #[proc_macro_attribute] 61 | pub fn query(attr: TokenStream, item: TokenStream) -> TokenStream { 62 | handle_debug_and_errors(export::ic_query, "ic_query", attr, item) 63 | } 64 | 65 | #[proc_macro_attribute] 66 | pub fn update(attr: TokenStream, item: TokenStream) -> TokenStream { 67 | handle_debug_and_errors(export::ic_update, "ic_update", attr, item) 68 | } 69 | 70 | #[proc_macro_attribute] 71 | pub fn init(attr: TokenStream, item: TokenStream) -> TokenStream { 72 | handle_debug_and_errors(export::ic_init, "ic_init", attr, item) 73 | } 74 | 75 | #[proc_macro_attribute] 76 | pub fn pre_upgrade(attr: TokenStream, item: TokenStream) -> TokenStream { 77 | handle_debug_and_errors(export::ic_pre_upgrade, "ic_pre_upgrade", attr, item) 78 | } 79 | 80 | #[proc_macro_attribute] 81 | pub fn post_upgrade(attr: TokenStream, item: TokenStream) -> TokenStream { 82 | handle_debug_and_errors(export::ic_post_upgrade, "ic_post_upgrade", attr, item) 83 | } 84 | 85 | #[proc_macro_attribute] 86 | pub fn heartbeat(attr: TokenStream, item: TokenStream) -> TokenStream { 87 | handle_debug_and_errors(export::ic_heartbeat, "ic_heartbeat", attr, item) 88 | } 89 | 90 | #[proc_macro_attribute] 91 | pub fn inspect_message(attr: TokenStream, item: TokenStream) -> TokenStream { 92 | handle_debug_and_errors(export::ic_inspect_message, "ic_inspect_message", attr, item) 93 | } 94 | 95 | #[proc_macro_attribute] 96 | pub fn on_low_wasm_memory(attr: TokenStream, item: TokenStream) -> TokenStream { 97 | handle_debug_and_errors( 98 | export::ic_on_low_wasm_memory, 99 | "ic_on_low_wasm_memory", 100 | attr, 101 | item, 102 | ) 103 | } 104 | -------------------------------------------------------------------------------- /e2e-tests/tests/macros.rs: -------------------------------------------------------------------------------- 1 | use candid::Principal; 2 | use prost::Message; 3 | 4 | mod canister { 5 | include!(concat!(env!("OUT_DIR"), "/canister.rs")); 6 | } 7 | use canister::*; 8 | 9 | mod test_utilities; 10 | use test_utilities::{cargo_build_canister, pic_base, update}; 11 | 12 | #[test] 13 | fn call_macros() { 14 | let wasm = cargo_build_canister("macros"); 15 | let pic = pic_base().build(); 16 | let canister_id = pic.create_canister(); 17 | pic.add_cycles(canister_id, 100_000_000_000_000); 18 | pic.install_canister(canister_id, wasm, vec![], None); 19 | let _: () = update(&pic, canister_id, "arg0", ()).unwrap(); 20 | let _: () = update(&pic, canister_id, "arg1", (1u32,)).unwrap(); 21 | let _: () = update(&pic, canister_id, "arg2", (1u32, 2u32)).unwrap(); 22 | let sender = Principal::anonymous(); 23 | let res = pic 24 | .update_call(canister_id, sender, "ret0", vec![]) 25 | .unwrap(); 26 | assert_eq!(res, vec![0]); 27 | let res = pic 28 | .update_call(canister_id, sender, "ret1", vec![]) 29 | .unwrap(); 30 | assert_eq!(res, vec![42]); 31 | let res = pic 32 | .update_call(canister_id, sender, "ret2", vec![]) 33 | .unwrap(); 34 | assert_eq!(res, vec![1, 2]); 35 | let res = pic 36 | .update_call( 37 | canister_id, 38 | sender, 39 | "method_one", 40 | MethodOneRequest { 41 | input: "Hello".to_string(), 42 | } 43 | .encode_to_vec(), 44 | ) 45 | .unwrap(); 46 | assert_eq!(res, MethodOneResponse { result: 5i32 }.encode_to_vec()); 47 | let res = pic 48 | .update_call( 49 | canister_id, 50 | sender, 51 | "method_two", 52 | MethodTwoRequest { values: vec![1.0] }.encode_to_vec(), 53 | ) 54 | .unwrap(); 55 | assert_eq!( 56 | res, 57 | MethodTwoResponse { 58 | success: true, 59 | message: "Hello world!".to_string() 60 | } 61 | .encode_to_vec() 62 | ); 63 | let _: (u32,) = update(&pic, canister_id, "manual_reply", ()).unwrap(); 64 | let (res,): (u32,) = update(&pic, canister_id, "generic", (1u32,)).unwrap(); 65 | assert_eq!(res, 2); 66 | 67 | let rej = pic 68 | .update_call(canister_id, sender, "with_guards", vec![1]) 69 | .unwrap_err(); 70 | assert_eq!(rej.reject_message, "guard1 failed"); 71 | let rej = pic 72 | .update_call(canister_id, sender, "with_guards", vec![3]) 73 | .unwrap_err(); 74 | assert_eq!(rej.reject_message, "guard2 failed"); 75 | let _res = pic 76 | .update_call(canister_id, sender, "with_guards", vec![15]) 77 | .unwrap(); 78 | 79 | // The entry-point expects an `opt nat32` value. 80 | // Here we send some blob that decoder need to skip. 81 | // The call is expected to: 82 | // * succeed: when the blob is relatively small 83 | // * fail: when the blob is too large 84 | let _: () = update( 85 | &pic, 86 | canister_id, 87 | "default_skipping_quota", 88 | (vec![42; 1400],), 89 | ) 90 | .unwrap(); 91 | let res: Result<(), _> = update( 92 | &pic, 93 | canister_id, 94 | "default_skipping_quota", 95 | (vec![42; 1500],), 96 | ); 97 | assert!(res 98 | .unwrap_err() 99 | .reject_message 100 | .contains("Skipping cost exceeds the limit")); 101 | } 102 | -------------------------------------------------------------------------------- /e2e-tests/tests/bindgen.rs: -------------------------------------------------------------------------------- 1 | mod test_utilities; 2 | use candid::Principal; 3 | use ic_cdk::management_canister::{CanisterSettings, EnvironmentVariable, UpdateSettingsArgs}; 4 | use test_utilities::{cargo_build_canister, pic_base, update}; 5 | 6 | #[test] 7 | fn bindgen() { 8 | let wasm = cargo_build_canister("bindgen"); 9 | let callee_wasm = cargo_build_canister("bindgen_callee"); 10 | 11 | let pic = pic_base().build(); 12 | 13 | let callee_canister_id = pic.create_canister(); 14 | pic.add_cycles(callee_canister_id, 100_000_000_000_000); 15 | pic.install_canister(callee_canister_id, callee_wasm, vec![], None); 16 | 17 | let canister_id = pic.create_canister(); 18 | pic.add_cycles(canister_id, 100_000_000_000_000); 19 | pic.install_canister(canister_id, wasm, vec![], None); 20 | // Make the callee canister a controller of the main canister. 21 | // We will impersonate a management canister call to set the env var later. 22 | pic.set_controllers(canister_id, None, vec![callee_canister_id]) 23 | .expect("failed to set controllers"); 24 | 25 | let _: () = update(&pic, canister_id, "call_management_canister", ()).unwrap(); 26 | 27 | // The required env var is not set, so the call will fail. 28 | let required_env_var = "ICP_CANISTER_ID:bindgen_callee"; 29 | let res: Result<(), _> = update(&pic, canister_id, "call_bindgen_callee", ()); 30 | assert!(res.unwrap_err().reject_message.contains(&format!( 31 | "env var `{}` is not set. Canister controller can set it using tools like icp-cli.", 32 | required_env_var 33 | ))); 34 | 35 | // Set a invalid value for the required env var. 36 | // Then the call will fail for a different reason. 37 | let effective_principal = 38 | pocket_ic::common::rest::RawEffectivePrincipal::CanisterId(canister_id.as_slice().to_vec()); 39 | let invalid_value = "Not a Principal"; 40 | let settings = CanisterSettings { 41 | environment_variables: Some(vec![EnvironmentVariable { 42 | name: required_env_var.into(), 43 | value: invalid_value.to_string(), 44 | }]), 45 | ..Default::default() 46 | }; 47 | let args = UpdateSettingsArgs { 48 | canister_id, 49 | settings, 50 | }; 51 | let _: () = pocket_ic::call_candid_as( 52 | &pic, 53 | Principal::management_canister(), 54 | effective_principal.clone(), 55 | callee_canister_id, 56 | "update_settings", 57 | (args,), 58 | ) 59 | .expect("failed to call update_settings for setting env var"); 60 | let res: Result<(), _> = update(&pic, canister_id, "call_bindgen_callee", ()); 61 | assert!(res.unwrap_err().reject_message.contains(&format!( 62 | "failed to parse Principal from env var `{}`, value `{}`", 63 | required_env_var, invalid_value 64 | ))); 65 | 66 | // Set the required env var. 67 | // This is expected to be set automatically by `icp-cli`. 68 | let settings = CanisterSettings { 69 | environment_variables: Some(vec![EnvironmentVariable { 70 | name: required_env_var.into(), 71 | value: callee_canister_id.to_string(), 72 | }]), 73 | ..Default::default() 74 | }; 75 | let args = UpdateSettingsArgs { 76 | canister_id, 77 | settings, 78 | }; 79 | 80 | let _: () = pocket_ic::call_candid_as( 81 | &pic, 82 | Principal::management_canister(), 83 | effective_principal, 84 | callee_canister_id, 85 | "update_settings", 86 | (args,), 87 | ) 88 | .expect("failed to call update_settings for setting env var"); 89 | let _: () = update(&pic, canister_id, "call_bindgen_callee", ()).unwrap(); 90 | } 91 | -------------------------------------------------------------------------------- /ic-management-canister-types/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [unreleased] 9 | 10 | ## [0.5.0] - 2025-11-13 11 | 12 | ### Changed 13 | 14 | - Removed `SettingsChange` variant from `ChangeDetails`. 15 | - Added `from_cansiter_id` field to `LoadSnapshotRecord`. 16 | - Added `ready_for_migration` and `version` fields to `CanisterStatusResult`. 17 | - Added `registry_version` field to `SubnetInfoResult`. 18 | 19 | Following changes are for the Candid interface changes proposed in this [forum post](https://forum.dfinity.org/t/proposal-making-variant-fields-optional-for-schema-evolution/57898#p-203913-proposed-solution-2). 20 | - The `details` field in `Change` is optional. 21 | - The `source` and `globals` fields in `ReadCanisterSnapshotMetadataResult` are optional. 22 | 23 | ### Added 24 | 25 | - `CanisterMetadataArgs` and `CanisterMetadataResult` for the new method `canister_metadata`. 26 | 27 | ## [0.4.1] - 2025-09-04 28 | 29 | ### Fixed 30 | 31 | - Used `candid:Reserved` inside `TakenFromCanister` and `MetadataUpload` variants in `SnapshotSource`. 32 | - Renamed `MainMemory` to `WasmMemory` in `SnapshotDataKind` and `SnapshotDataOffset`. 33 | - Added `source` field to `LoadSnapshotRecord`. 34 | 35 | While this is technically a breaking change in the Rust type system, we are treating it as a patch fix. 36 | This is because the affected types and methods are for new, unreleased features (snapshot download/upload). 37 | Therefore, no existing services or canisters should be impacted by this change. 38 | 39 | ## [0.4.0] - 2025-08-25 40 | 41 | ### Changed 42 | 43 | - Added `environment_variable` field to `CanisterSettings` and `DefiniteCanisterSettings`. 44 | - Added the type `EnvironmentVariable`. 45 | - Added `settings_change` variant to `ChangeDetails`. 46 | - Added `environment_variables_hash` field to `CreationRecord`. 47 | - Added `is_replicated` field to `HttpRequestArgs`. 48 | 49 | ## [0.3.3] - 2025-08-20 50 | 51 | ### Fixed 52 | 53 | - The `exported_globals` field in the `ReadCanisterSnapshotMetadataResult` and `UploadCanisterSnapshotMetadataArgs` structs has been renamed to `globals`. 54 | - The associated type `ExportedGlobal` has been renamed to `SnapshotMetadataGlobal`. 55 | 56 | While this is technically a breaking change in the Rust type system, we are treating it as a patch fix. 57 | This is because the affected types and methods are for new, unreleased features (snapshot download/upload). 58 | Therefore, no existing services or canisters should be impacted by this change. 59 | 60 | ## [0.3.2] - 2025-07-25 61 | 62 | ### Added 63 | 64 | - Types for canister snapshot download/upload. 65 | 66 | ## [0.3.1] - 2025-05-09 67 | 68 | ### Added 69 | 70 | - Types for `vetkd_public_key` and `vetkd_derive_key`. 71 | 72 | ## [0.3.0] - 2025-03-17 73 | 74 | ### Changed 75 | 76 | - Added `wasm_memory_threshold` field to `CanisterSettings` and `DefiniteCanisterSettings`. 77 | - Added the `memory_metrics` field to `CanisterStatusResult`. 78 | - Added the type `MemoryMetrics`. 79 | 80 | ### Added 81 | 82 | - Implemented trait that convert from `EcdsaCurve` and `SchnorrAlgorithm` into `u32`. 83 | 84 | ## [0.2.1] - 2025-02-28 85 | 86 | ### Added 87 | 88 | - Types for `fetch_canister_logs`. 89 | - `CanisterIdRecord`, an alias for various argument and result types to enhance inter-operability. 90 | 91 | ### Fixed 92 | 93 | - Doc: `HttpRequestArgs::max_response_bytes` is capped at 2MB, not 2MiB. 94 | 95 | ## [0.2.0] - 2025-02-18 96 | 97 | ### Changed 98 | 99 | - Added `aux` field in `SignWithSchnorrArgs`, introducing `SchnorrAux` and `Bip341` types. 100 | - Fixed `NodeMetrics` which should have a field `num_block_failures_total`, not `num_blocks_failures_total`. 101 | 102 | ## [0.1.0] - 2023-01-22 103 | 104 | ### Added 105 | 106 | - Initial release of the `ic-management-canister-types` library. 107 | -------------------------------------------------------------------------------- /legacy/ic-response-codes/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc = include_str!("../README.md")] 2 | 3 | use std::error::Error; 4 | use std::fmt::Display; 5 | 6 | /// Classifies why an API request or inter-canister call in the IC is rejected. 7 | /// 8 | /// # Note 9 | /// 10 | /// Zero (0) is not a valid reject code. 11 | /// Converting 0 into this enum will return an error. 12 | /// 13 | /// See [Reject codes](https://internetcomputer.org/docs/current/references/ic-interface-spec/#reject-codes) for more details. 14 | #[repr(u32)] 15 | #[derive(Clone, Copy, Hash, Debug, PartialEq, Eq, PartialOrd, Ord)] 16 | pub enum RejectCode { 17 | /// Fatal system error, retry unlikely to be useful. 18 | SysFatal = 1, 19 | /// Transient system error, retry might be possible. 20 | SysTransient = 2, 21 | /// Invalid destination (e.g. canister/account does not exist). 22 | DestinationInvalid = 3, 23 | /// Explicit reject by the canister. 24 | CanisterReject = 4, 25 | /// Canister error (e.g., trap, no response). 26 | CanisterError = 5, 27 | /// Response unknown; system stopped waiting for it (e.g., timed out, or system under high load). 28 | SysUnknown = 6, 29 | 30 | /// Unrecognized reject code. 31 | /// 32 | /// # Note 33 | /// 34 | /// This variant is not part of the IC interface spec, and is used to represent 35 | /// reject codes that are not recognized by the library. 36 | /// 37 | /// This variant is needed just in case the IC introduces new reject codes in the future. 38 | /// If that happens, a Canister using existing library versions will still be able to convert 39 | /// the new reject codes to this variant without panicking. 40 | Unrecognized(u32), 41 | } 42 | 43 | impl Display for RejectCode { 44 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 45 | match self { 46 | RejectCode::SysFatal => write!(f, "SysFatal(1)"), 47 | RejectCode::SysTransient => write!(f, "SysTransient(2)"), 48 | RejectCode::DestinationInvalid => write!(f, "DestinationInvalid(3)"), 49 | RejectCode::CanisterReject => write!(f, "CanisterReject(4)"), 50 | RejectCode::CanisterError => write!(f, "CanisterError(5)"), 51 | RejectCode::SysUnknown => write!(f, "SysUnknown(6)"), 52 | RejectCode::Unrecognized(code) => write!(f, "Unrecognized({})", code), 53 | } 54 | } 55 | } 56 | 57 | /// Error type for [`RejectCode`] conversion. 58 | /// 59 | /// The only case where this error can occur is when trying to convert a 0 to a [`RejectCode`]. 60 | #[derive(Clone, Copy, Debug)] 61 | pub struct ZeroIsInvalidRejectCode; 62 | 63 | impl Display for ZeroIsInvalidRejectCode { 64 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 65 | write!(f, "zero is invalid reject code") 66 | } 67 | } 68 | 69 | impl Error for ZeroIsInvalidRejectCode {} 70 | 71 | impl TryFrom for RejectCode { 72 | type Error = ZeroIsInvalidRejectCode; 73 | 74 | fn try_from(code: u32) -> Result { 75 | match code { 76 | 0 => Err(ZeroIsInvalidRejectCode), 77 | 1 => Ok(RejectCode::SysFatal), 78 | 2 => Ok(RejectCode::SysTransient), 79 | 3 => Ok(RejectCode::DestinationInvalid), 80 | 4 => Ok(RejectCode::CanisterReject), 81 | 5 => Ok(RejectCode::CanisterError), 82 | 6 => Ok(RejectCode::SysUnknown), 83 | _ => Ok(RejectCode::Unrecognized(code)), 84 | } 85 | } 86 | } 87 | 88 | impl From for u32 { 89 | fn from(code: RejectCode) -> u32 { 90 | match code { 91 | RejectCode::SysFatal => 1, 92 | RejectCode::SysTransient => 2, 93 | RejectCode::DestinationInvalid => 3, 94 | RejectCode::CanisterReject => 4, 95 | RejectCode::CanisterError => 5, 96 | RejectCode::SysUnknown => 6, 97 | RejectCode::Unrecognized(code) => code, 98 | } 99 | } 100 | } 101 | 102 | impl PartialEq for RejectCode { 103 | fn eq(&self, other: &u32) -> bool { 104 | let self_as_u32: u32 = (*self).into(); 105 | self_as_u32 == *other 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /ic-cdk/tests/bitcoin.did: -------------------------------------------------------------------------------- 1 | type network = variant { 2 | mainnet; 3 | testnet; // Bitcoin testnet4. 4 | regtest; 5 | }; 6 | 7 | type satoshi = nat64; 8 | 9 | type address = text; 10 | 11 | type block_hash = blob; 12 | 13 | type block_header = blob; 14 | 15 | type block_height = nat32; 16 | 17 | type outpoint = record { 18 | txid : blob; 19 | vout : nat32; 20 | }; 21 | 22 | type utxo = record { 23 | outpoint : outpoint; 24 | value : satoshi; 25 | height : block_height; 26 | }; 27 | 28 | type flag = variant { 29 | enabled; 30 | disabled; 31 | }; 32 | 33 | type init_config = record { 34 | stability_threshold : opt nat; 35 | network : opt network; 36 | blocks_source : opt principal; 37 | syncing : opt flag; 38 | fees : opt fees; 39 | api_access : opt flag; 40 | disable_api_if_not_fully_synced : opt flag; 41 | watchdog_canister : opt opt principal; 42 | burn_cycles : opt flag; 43 | lazily_evaluate_fee_percentiles : opt flag; 44 | }; 45 | 46 | type config = record { 47 | stability_threshold : nat; 48 | network : network; 49 | blocks_source : principal; 50 | syncing : flag; 51 | fees : fees; 52 | api_access : flag; 53 | disable_api_if_not_fully_synced : flag; 54 | watchdog_canister : opt principal; 55 | burn_cycles : flag; 56 | lazily_evaluate_fee_percentiles : flag; 57 | }; 58 | 59 | type fees = record { 60 | get_utxos_base : nat; 61 | get_utxos_cycles_per_ten_instructions : nat; 62 | get_utxos_maximum : nat; 63 | get_balance : nat; 64 | get_balance_maximum : nat; 65 | get_current_fee_percentiles : nat; 66 | get_current_fee_percentiles_maximum : nat; 67 | send_transaction_base : nat; 68 | send_transaction_per_byte : nat; 69 | get_block_headers_base : nat; 70 | get_block_headers_cycles_per_ten_instructions : nat; 71 | get_block_headers_maximum : nat; 72 | }; 73 | 74 | type get_balance_request = record { 75 | network : network; 76 | address : address; 77 | min_confirmations : opt nat32; 78 | }; 79 | 80 | type get_utxos_request = record { 81 | network : network; 82 | address : address; 83 | filter : opt variant { 84 | min_confirmations : nat32; 85 | page : blob; 86 | }; 87 | }; 88 | 89 | type get_utxos_response = record { 90 | utxos : vec utxo; 91 | tip_block_hash : block_hash; 92 | tip_height : block_height; 93 | next_page : opt blob; 94 | }; 95 | 96 | type get_current_fee_percentiles_request = record { 97 | network : network; 98 | }; 99 | 100 | type send_transaction_request = record { 101 | network : network; 102 | transaction : blob; 103 | }; 104 | 105 | type millisatoshi_per_byte = nat64; 106 | 107 | type set_config_request = record { 108 | stability_threshold : opt nat; 109 | syncing : opt flag; 110 | fees : opt fees; 111 | api_access : opt flag; 112 | disable_api_if_not_fully_synced : opt flag; 113 | watchdog_canister : opt opt principal; 114 | burn_cycles : opt flag; 115 | lazily_evaluate_fee_percentiles : opt flag; 116 | }; 117 | 118 | type get_block_headers_request = record { 119 | start_height : block_height; 120 | end_height : opt block_height; 121 | network : network; 122 | }; 123 | 124 | type get_block_headers_response = record { 125 | tip_height : block_height; 126 | block_headers : vec block_header; 127 | }; 128 | 129 | // service bitcoin : (init_config) -> { 130 | service bitcoin : { 131 | bitcoin_get_balance : (get_balance_request) -> (satoshi); 132 | 133 | // bitcoin_get_balance_query : (get_balance_request) -> (satoshi) query; 134 | 135 | bitcoin_get_utxos : (get_utxos_request) -> (get_utxos_response); 136 | 137 | // bitcoin_get_utxos_query : (get_utxos_request) -> (get_utxos_response) query; 138 | 139 | bitcoin_get_current_fee_percentiles : (get_current_fee_percentiles_request) -> (vec millisatoshi_per_byte); 140 | 141 | bitcoin_get_block_headers : (get_block_headers_request) -> (get_block_headers_response); 142 | 143 | bitcoin_send_transaction : (send_transaction_request) -> (); 144 | 145 | // get_config : () -> (config) query; 146 | 147 | // set_config : (set_config_request) -> (); 148 | }; 149 | -------------------------------------------------------------------------------- /ic-cdk/README.md: -------------------------------------------------------------------------------- 1 | [![Documentation](https://docs.rs/ic-cdk/badge.svg)](https://docs.rs/ic-cdk/) 2 | [![Crates.io](https://img.shields.io/crates/v/ic-cdk.svg)](https://crates.io/crates/ic-cdk) 3 | [![License](https://img.shields.io/crates/l/ic-cdk.svg)](https://github.com/dfinity/cdk-rs/blob/main/LICENSE) 4 | [![Downloads](https://img.shields.io/crates/d/ic-cdk.svg)](https://crates.io/crates/ic-cdk) 5 | [![CI](https://github.com/dfinity/cdk-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/dfinity/cdk-rs/actions/workflows/ci.yml) 6 | 7 | # ic-cdk 8 | 9 | Canister Developer Kit for the Internet Computer. 10 | 11 | ## Background 12 | 13 | On the Internet Computer, smart contracts come in the form of canisters which are WebAssembly modules. 14 | 15 | Canisters expose entry points which can be called both by other canisters and by parties external to the IC. 16 | 17 | This library aims to provide a Rust-ergonomic abstraction to implement Canister entry points. 18 | 19 | ## Getting Started 20 | 21 | In Cargo.toml: 22 | 23 | ```toml 24 | [lib] 25 | crate-type = ["cdylib"] 26 | 27 | [dependencies] 28 | ic-cdk = "0.19" 29 | candid = "0.10" # required if you want to define Candid data types 30 | ``` 31 | 32 | Then in Rust source code: 33 | 34 | ```rust 35 | #[ic_cdk::query] 36 | fn hello() -> String { 37 | "world".to_string() 38 | } 39 | ``` 40 | 41 | This will register a **query** entry point named `hello`. 42 | 43 | ## Compilation 44 | 45 | ### Stable Target: `wasm32-unknown-unknown` 46 | 47 | ```sh 48 | cargo build --target wasm32-unknown-unknown 49 | ``` 50 | 51 | ### Experimental Target: `wasm64-unknown-unknown` 52 | 53 | No changes to the source code are required. However, setting up the Rust toolchain for Wasm64 support requires some additional steps. 54 | 55 | 1. Install nightly toolchain: 56 | ```bash 57 | rustup toolchain install nightly 58 | ``` 59 | 2. Add rust-src component: 60 | ```bash 61 | rustup component add rust-src --toolchain nightly 62 | ``` 63 | 3. Build with necessary flags: 64 | ```bash 65 | cargo +nightly build -Z build-std=std,panic_abort --target wasm64-unknown-unknown 66 | ``` 67 | 68 | ## Macros 69 | 70 | This library re-exports macros defined in `ic-cdk-macros` crate. 71 | 72 | The macros fall into two categories: 73 | 74 | * To register functions as canister entry points 75 | * To export Candid definitions 76 | 77 | ### Register functions as canister entry points 78 | 79 | These macros are directly related to the [Internet Computer Specification](https://internetcomputer.org/docs/current/references/ic-interface-spec#entry-points). 80 | 81 | * [`init`](https://docs.rs/ic-cdk/latest/ic_cdk/attr.init.html) 82 | * [`pre_upgrade`](https://docs.rs/ic-cdk/latest/ic_cdk/attr.pre_upgrade.html) 83 | * [`post_upgrade`](https://docs.rs/ic-cdk/latest/ic_cdk/attr.post_upgrade.html) 84 | * [`inspect_message`](https://docs.rs/ic-cdk/latest/ic_cdk/attr.inspect_message.html) 85 | * [`heartbeat`](https://docs.rs/ic-cdk/latest/ic_cdk/attr.heartbeat.html) 86 | * [`on_low_wasm_memory`](https://docs.rs/ic-cdk/latest/ic_cdk/attr.on_low_wasm_memory.html) 87 | * [`update`](https://docs.rs/ic-cdk/latest/ic_cdk/attr.update.html) 88 | * [`query`](https://docs.rs/ic-cdk/latest/ic_cdk/attr.query.html) 89 | 90 | Canister entry points can be `async`. The CDK embeds an asynchronous executor. Unfortunately anything `tokio`-specific cannot be used. 91 | Use the [`spawn`](https://docs.rs/ic-cdk/latest/ic_cdk/futures/fn.spawn.html) function to run more asynchronous functions in 92 | the background. Panics can cause async tasks to cancel partway through; read the documentation for the 93 | [`futures`](https://docs.rs/ic-cdk/latest/ic_cdk/futures/index.html) module for more information. 94 | 95 | ### Export Candid definitions 96 | 97 | * [`export_candid`](https://docs.rs/ic-cdk/latest/ic_cdk/macro.export_candid.html) 98 | 99 | Check [Generating Candid files for Rust canisters](https://internetcomputer.org/docs/current/developer-docs/backend/candid/generating-candid/) for more details. 100 | 101 | ## More examples 102 | 103 | The [examples repository](https://github.com/dfinity/examples/tree/master/rust) offers numerous Rust examples demonstrating how to build functional Rust canisters. 104 | 105 | ## Manage Data Structures in Stable Memory 106 | 107 | For managing larger datasets and multiple data structures in stable memory, consider using the [`ic-stable-structures`](https://crates.io/crates/ic-stable-structures) crate. While the `ic_cdk::storage::{stable_save, stable_restore}` API is straightforward, it may not be efficient for larger datasets. The `ic-stable-structures` crate provides more scalable solutions for such scenarios. 108 | -------------------------------------------------------------------------------- /e2e-tests/src/bin/http_request.rs: -------------------------------------------------------------------------------- 1 | use ic_cdk::management_canister::{ 2 | http_request, http_request_with_closure, transform_context_from_query, HttpHeader, HttpMethod, 3 | HttpRequestArgs, HttpRequestResult, TransformArgs, 4 | }; 5 | use ic_cdk::{query, update}; 6 | 7 | /// All fields are Some except transform. 8 | #[update] 9 | async fn get_without_transform() { 10 | let args = HttpRequestArgs { 11 | url: "https://example.com".to_string(), 12 | method: HttpMethod::GET, 13 | headers: vec![HttpHeader { 14 | name: "request_header_name".to_string(), 15 | value: "request_header_value".to_string(), 16 | }], 17 | body: Some(vec![1]), 18 | max_response_bytes: Some(100_000), 19 | transform: None, 20 | is_replicated: Some(true), 21 | }; 22 | 23 | let res = http_request(&args).await.unwrap(); 24 | assert_eq!(res.status, 200u32); 25 | assert_eq!( 26 | res.headers, 27 | vec![HttpHeader { 28 | name: "response_header_name".to_string(), 29 | value: "response_header_value".to_string(), 30 | }] 31 | ); 32 | assert_eq!(res.body, vec![42]); 33 | } 34 | 35 | /// Method is POST. 36 | #[update] 37 | async fn post() { 38 | let args = HttpRequestArgs { 39 | url: "https://example.com".to_string(), 40 | method: HttpMethod::POST, 41 | ..Default::default() 42 | }; 43 | 44 | http_request(&args).await.unwrap(); 45 | } 46 | 47 | /// Method is HEAD. 48 | #[update] 49 | async fn head() { 50 | let args = HttpRequestArgs { 51 | url: "https://example.com".to_string(), 52 | method: HttpMethod::HEAD, 53 | ..Default::default() 54 | }; 55 | 56 | http_request(&args).await.unwrap(); 57 | } 58 | 59 | /// The standard way to define a transform function. 60 | /// 61 | /// It is a query method that takes a `TransformArgs` and returns an `HttpRequestResult`. 62 | #[query] 63 | fn transform(args: TransformArgs) -> HttpRequestResult { 64 | let mut body = args.response.body; 65 | body.push(args.context[0]); 66 | HttpRequestResult { 67 | status: args.response.status, 68 | headers: args.response.headers, 69 | body, 70 | } 71 | } 72 | 73 | /// Set the transform field with the name of the transform query method. 74 | #[update] 75 | async fn get_with_transform() { 76 | let args = HttpRequestArgs { 77 | url: "https://example.com".to_string(), 78 | method: HttpMethod::GET, 79 | transform: Some(transform_context_from_query( 80 | "transform".to_string(), 81 | vec![42], 82 | )), 83 | ..Default::default() 84 | }; 85 | 86 | let res = http_request(&args).await.unwrap(); 87 | assert_eq!(res.status, 200u32); 88 | assert_eq!( 89 | res.headers, 90 | vec![HttpHeader { 91 | name: "response_header_name".to_string(), 92 | value: "response_header_value".to_string(), 93 | }] 94 | ); 95 | // The first 42 is from the response body, the second 42 is from the transform context. 96 | assert_eq!(res.body, vec![42, 42]); 97 | } 98 | 99 | /// Set the transform field with a closure. 100 | #[update] 101 | async fn get_with_transform_closure() { 102 | let transform = |args: HttpRequestResult| { 103 | let mut body = args.body; 104 | body.push(42); 105 | HttpRequestResult { 106 | status: args.status, 107 | headers: args.headers, 108 | body, 109 | } 110 | }; 111 | let args = HttpRequestArgs { 112 | url: "https://example.com".to_string(), 113 | method: HttpMethod::GET, 114 | transform: None, 115 | ..Default::default() 116 | }; 117 | let res = http_request_with_closure(&args, transform).await.unwrap(); 118 | assert_eq!(res.status, 200u32); 119 | assert_eq!( 120 | res.headers, 121 | vec![HttpHeader { 122 | name: "response_header_name".to_string(), 123 | value: "response_header_value".to_string(), 124 | }] 125 | ); 126 | // The first 42 is from the response body, the second 42 is from the transform closure. 127 | assert_eq!(res.body, vec![42, 42]); 128 | } 129 | 130 | /// Non replicated HTTP request. 131 | #[update] 132 | async fn non_replicated() { 133 | let args = HttpRequestArgs { 134 | url: "https://example.com".to_string(), 135 | method: HttpMethod::GET, 136 | is_replicated: Some(false), 137 | ..Default::default() 138 | }; 139 | 140 | http_request(&args).await.unwrap(); 141 | } 142 | 143 | fn main() {} 144 | -------------------------------------------------------------------------------- /ic-cdk-timers/src/state.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | cell::{Cell, RefCell}, 3 | cmp::Ordering, 4 | collections::BinaryHeap, 5 | pin::Pin, 6 | time::Duration, 7 | }; 8 | 9 | use slotmap::{SlotMap, new_key_type}; 10 | 11 | // To ensure that tasks are removable seamlessly, there are two separate concepts here: 12 | // tasks, for the actual function being called, and timers, the scheduled execution of tasks. 13 | // As this is an implementation detail, lib.rs exposes TaskId under the name TimerId. 14 | 15 | thread_local! { 16 | pub(crate) static TIMER_COUNTER: Cell = const { Cell::new(0) }; 17 | pub(crate) static TASKS: RefCell> = RefCell::default(); 18 | pub(crate) static TIMERS: RefCell> = RefCell::default(); 19 | static MOST_RECENT: Cell> = const { Cell::new(None) }; 20 | pub(crate) static ALL_CALLS: Cell = const { Cell::new(0) }; 21 | } 22 | 23 | pub(crate) enum Task { 24 | Once(Pin>>), 25 | Repeated { 26 | func: Box Pin>>>, 27 | interval: Duration, 28 | concurrent_calls: usize, 29 | }, 30 | RepeatedSerial { 31 | func: Box, 32 | interval: Duration, 33 | }, 34 | RepeatedSerialBusy { 35 | interval: Duration, 36 | }, 37 | Invalid, 38 | } 39 | 40 | pub(crate) trait SerialClosure { 41 | fn call<'a>(&'a mut self) -> Pin + 'a>>; 42 | } 43 | 44 | impl SerialClosure for F { 45 | fn call<'a>(&'a mut self) -> Pin + 'a>> { 46 | Box::pin(self()) 47 | } 48 | } 49 | 50 | new_key_type! { 51 | #[expect(missing_docs)] // documented in lib.rs 52 | pub struct TaskId; 53 | } 54 | 55 | #[derive(Debug)] 56 | pub(crate) struct Timer { 57 | pub(crate) task: TaskId, 58 | pub(crate) time: u64, 59 | pub(crate) counter: u128, 60 | } 61 | 62 | // Timers are sorted first by time, then by insertion order to ensure deterministic ordering. 63 | // The ordering is reversed (earlier timer > later) for use in BinaryHeap which is a max-heap. 64 | 65 | impl Ord for Timer { 66 | fn cmp(&self, other: &Self) -> Ordering { 67 | self.time 68 | .cmp(&other.time) 69 | .then_with(|| self.counter.cmp(&other.counter)) 70 | .reverse() 71 | } 72 | } 73 | 74 | impl PartialOrd for Timer { 75 | fn partial_cmp(&self, other: &Self) -> Option { 76 | Some(self.cmp(other)) 77 | } 78 | } 79 | 80 | impl PartialEq for Timer { 81 | fn eq(&self, other: &Self) -> bool { 82 | self.time == other.time 83 | } 84 | } 85 | 86 | impl Eq for Timer {} 87 | 88 | pub(crate) fn next_counter() -> u128 { 89 | TIMER_COUNTER.with(|c| { 90 | let v = c.get(); 91 | c.set(v + 1); 92 | v 93 | }) 94 | } 95 | 96 | /// Calls `ic0.global_timer_set` with the soonest timer in [`TIMERS`]. This is needed after inserting a timer, and after executing one. 97 | pub(crate) fn update_ic0_timer() { 98 | TIMERS.with_borrow(|timers| { 99 | let soonest_timer = timers.peek().map(|timer| timer.time); 100 | let should_change = match (soonest_timer, MOST_RECENT.get()) { 101 | (Some(timer), Some(recent)) => timer < recent, 102 | (Some(_), None) => true, 103 | _ => false, 104 | }; 105 | if should_change { 106 | ic0::global_timer_set(soonest_timer.unwrap()); 107 | MOST_RECENT.set(soonest_timer); 108 | } 109 | }); 110 | } 111 | 112 | /// Like [`update_ic0_timer`], but forces updating unconditionally. Should only be called from canister_global_timer. 113 | pub(crate) fn update_ic0_timer_clean() { 114 | MOST_RECENT.set(None); 115 | update_ic0_timer(); 116 | } 117 | 118 | impl Task { 119 | pub(crate) fn increment_concurrent(&mut self) { 120 | if let Task::Repeated { 121 | concurrent_calls, .. 122 | } = self 123 | { 124 | *concurrent_calls += 1; 125 | } 126 | } 127 | pub(crate) fn decrement_concurrent(&mut self) { 128 | if let Task::Repeated { 129 | concurrent_calls, .. 130 | } = self 131 | { 132 | if *concurrent_calls > 0 { 133 | *concurrent_calls -= 1; 134 | } 135 | } 136 | } 137 | } 138 | 139 | pub(crate) fn increment_all_calls() { 140 | ALL_CALLS.set(ALL_CALLS.get() + 1); 141 | } 142 | 143 | pub(crate) fn decrement_all_calls() { 144 | let current = ALL_CALLS.get(); 145 | if current > 0 { 146 | ALL_CALLS.set(current - 1); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /ic-cdk/src/api/management_canister/http_request/mod.rs: -------------------------------------------------------------------------------- 1 | //! Canister HTTP request. 2 | 3 | use crate::api::call::{CallResult, call_with_payment128}; 4 | use candid::Principal; 5 | #[cfg(feature = "transform-closure")] 6 | use slotmap::{DefaultKey, Key, KeyData, SlotMap}; 7 | #[cfg(feature = "transform-closure")] 8 | use std::cell::RefCell; 9 | 10 | mod types; 11 | pub use types::*; 12 | 13 | /// Make an HTTP request to a given URL and return the HTTP response, possibly after a transformation. 14 | /// 15 | /// See [IC method `http_request`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-http_request). 16 | /// 17 | /// This call requires cycles payment. The required cycles is a function of the request size and `max_response_bytes`. 18 | /// Check [Gas and cycles cost](https://internetcomputer.org/docs/current/developer-docs/gas-cost) for more details. 19 | pub async fn http_request( 20 | arg: CanisterHttpRequestArgument, 21 | cycles: u128, 22 | ) -> CallResult<(HttpResponse,)> { 23 | call_with_payment128( 24 | Principal::management_canister(), 25 | "http_request", 26 | (arg,), 27 | cycles, 28 | ) 29 | .await 30 | } 31 | 32 | #[cfg(feature = "transform-closure")] 33 | thread_local! { 34 | #[allow(clippy::type_complexity)] 35 | static TRANSFORMS_LEGACY: RefCell HttpResponse>>> = RefCell::default(); 36 | } 37 | 38 | #[cfg(feature = "transform-closure")] 39 | #[cfg_attr( 40 | target_family = "wasm", 41 | unsafe(export_name = "canister_query http_transform_legacy") 42 | )] 43 | #[cfg_attr( 44 | not(target_family = "wasm"), 45 | unsafe(export_name = "canister_query_ic_cdk_internal.http_transform_legacy") 46 | )] 47 | extern "C" fn http_transform() { 48 | ic_cdk_executor::in_tracking_query_executor_context(|| { 49 | use crate::api::{ 50 | call::{ArgDecoderConfig, arg_data, reply}, 51 | caller, 52 | }; 53 | if caller() != Principal::management_canister() { 54 | crate::trap("This function is internal to ic-cdk and should not be called externally."); 55 | } 56 | let (args,): (TransformArgs,) = arg_data(ArgDecoderConfig::default()); 57 | let int = u64::from_be_bytes(args.context[..].try_into().unwrap()); 58 | let key = DefaultKey::from(KeyData::from_ffi(int)); 59 | let func = TRANSFORMS_LEGACY.with(|transforms| transforms.borrow_mut().remove(key)); 60 | let Some(func) = func else { 61 | crate::trap(format!("Missing transform function for request {int}")); 62 | }; 63 | let transformed = func(args.response); 64 | reply((transformed,)); 65 | }); 66 | } 67 | 68 | /// Make an HTTP request to a given URL and return the HTTP response, after a transformation. 69 | /// 70 | /// Do not set the `transform` field of `arg`. To use a Candid function, call [`http_request`] instead. 71 | /// 72 | /// See [IC method `http_request`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-http_request). 73 | /// 74 | /// This call requires cycles payment. The required cycles is a function of the request size and `max_response_bytes`. 75 | /// Check [Gas and cycles cost](https://internetcomputer.org/docs/current/developer-docs/gas-cost) for more details. 76 | #[cfg(feature = "transform-closure")] 77 | #[cfg_attr(docsrs, doc(cfg(feature = "transform-closure")))] 78 | pub async fn http_request_with_closure( 79 | arg: CanisterHttpRequestArgument, 80 | cycles: u128, 81 | transform_func: impl FnOnce(HttpResponse) -> HttpResponse + 'static, 82 | ) -> CallResult<(HttpResponse,)> { 83 | assert!( 84 | arg.transform.is_none(), 85 | "`CanisterHttpRequestArgument`'s `transform` field must be `None` when using a closure" 86 | ); 87 | let transform_func = Box::new(transform_func) as _; 88 | let key = TRANSFORMS_LEGACY.with(|transforms| transforms.borrow_mut().insert(transform_func)); 89 | struct DropGuard(DefaultKey); 90 | impl Drop for DropGuard { 91 | fn drop(&mut self) { 92 | TRANSFORMS_LEGACY.with(|transforms| transforms.borrow_mut().remove(self.0)); 93 | } 94 | } 95 | let key = DropGuard(key); 96 | let context = key.0.data().as_ffi().to_be_bytes().to_vec(); 97 | let arg = CanisterHttpRequestArgument { 98 | transform: Some(TransformContext { 99 | function: TransformFunc(candid::Func { 100 | method: " http_transform_legacy".into(), 101 | principal: crate::id(), 102 | }), 103 | context, 104 | }), 105 | ..arg 106 | }; 107 | http_request(arg, cycles).await 108 | } 109 | -------------------------------------------------------------------------------- /ic-cdk/src/api/management_canister/http_request/types.rs: -------------------------------------------------------------------------------- 1 | use crate::id; 2 | use candid::CandidType; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | mod transform { 6 | #![allow(missing_docs)] 7 | 8 | // The struct `TransformFunc` is defined by a macro. 9 | // Adding doc comment directly above the macro doesn't work. 10 | // The workaround is to re-export it and document there. 11 | // TODO: upgrade Rust toolchain (https://dfinity.atlassian.net/browse/SDK-1183) 12 | use super::*; 13 | 14 | candid::define_function!(pub TransformFunc : (TransformArgs) -> (HttpResponse) query); 15 | } 16 | 17 | /// "transform" function of type: `func (http_response) -> (http_response) query` 18 | pub use transform::TransformFunc; 19 | 20 | /// Type used for encoding/decoding: 21 | /// `record { 22 | /// response : http_response; 23 | /// context : blob; 24 | /// }` 25 | #[derive(CandidType, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] 26 | pub struct TransformArgs { 27 | /// Raw response from remote service, to be transformed 28 | pub response: HttpResponse, 29 | 30 | /// Context for response transformation 31 | #[serde(with = "serde_bytes")] 32 | pub context: Vec, 33 | } 34 | 35 | /// Type used for encoding/decoding: 36 | /// `record { 37 | /// function : func (record {response : http_response; context : blob}) -> (http_response) query; 38 | /// context : blob; 39 | /// }` 40 | #[derive(CandidType, Clone, Debug, Deserialize, PartialEq, Eq)] 41 | pub struct TransformContext { 42 | /// Reference function with signature: `func (record {response : http_response; context : blob}) -> (http_response) query;`. 43 | pub function: TransformFunc, 44 | 45 | /// Context to be passed to `transform` function to transform HTTP response for consensus 46 | #[serde(with = "serde_bytes")] 47 | pub context: Vec, 48 | } 49 | 50 | impl TransformContext { 51 | /// Constructs a `TransformContext` from a name and context. The principal is assumed to be the [current canister's](id). 52 | pub fn from_name(candid_function_name: String, context: Vec) -> Self { 53 | Self { 54 | context, 55 | function: TransformFunc(candid::Func { 56 | method: candid_function_name, 57 | principal: id(), 58 | }), 59 | } 60 | } 61 | } 62 | 63 | /// HTTP header. 64 | #[derive( 65 | CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, 66 | )] 67 | pub struct HttpHeader { 68 | /// Name 69 | pub name: String, 70 | /// Value 71 | pub value: String, 72 | } 73 | 74 | /// HTTP method. 75 | /// 76 | /// Currently support following methods. 77 | #[derive( 78 | CandidType, 79 | Serialize, 80 | Deserialize, 81 | Debug, 82 | PartialEq, 83 | Eq, 84 | PartialOrd, 85 | Ord, 86 | Hash, 87 | Clone, 88 | Copy, 89 | Default, 90 | )] 91 | pub enum HttpMethod { 92 | /// GET 93 | #[serde(rename = "get")] 94 | #[default] 95 | GET, 96 | /// POST 97 | #[serde(rename = "post")] 98 | POST, 99 | /// HEAD 100 | #[serde(rename = "head")] 101 | HEAD, 102 | } 103 | 104 | /// Argument type of [`http_request`](super::http_request). 105 | #[derive(CandidType, Deserialize, Debug, PartialEq, Eq, Clone, Default)] 106 | pub struct CanisterHttpRequestArgument { 107 | /// The requested URL. 108 | pub url: String, 109 | /// The maximal size of the response in bytes. If None, 2MiB will be the limit. 110 | /// This value affects the cost of the http request and it is highly recommended 111 | /// to set it as low as possible to avoid unnecessary extra costs. 112 | /// See also the [pricing section of HTTP outcalls documentation](https://internetcomputer.org/docs/current/developer-docs/integrations/http_requests/http_requests-how-it-works#pricing). 113 | pub max_response_bytes: Option, 114 | /// The method of HTTP request. 115 | pub method: HttpMethod, 116 | /// List of HTTP request headers and their corresponding values. 117 | pub headers: Vec, 118 | /// Optionally provide request body. 119 | pub body: Option>, 120 | /// Name of the transform function which is `func (transform_args) -> (http_response) query`. 121 | /// Set to `None` if you are using `http_request_with` or `http_request_with_cycles_with`. 122 | pub transform: Option, 123 | } 124 | 125 | /// The returned HTTP response. 126 | #[derive( 127 | CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, 128 | )] 129 | pub struct HttpResponse { 130 | /// The response status (e.g., 200, 404). 131 | pub status: candid::Nat, 132 | /// List of HTTP response headers and their corresponding values. 133 | pub headers: Vec, 134 | /// The response’s body. 135 | pub body: Vec, 136 | } 137 | -------------------------------------------------------------------------------- /ic-cdk-bindgen/README.md: -------------------------------------------------------------------------------- 1 | # ic-cdk-bindgen 2 | 3 | Bindings generator from Candid to Rust for `ic-cdk`. 4 | 5 | `ic-cdk-bindgen` is designed to be used for build-time code generation as part of a Cargo build-script. 6 | 7 | ## Example 8 | 9 | The goal is to conveniently make inter-canister calls to the canister `callee`. 10 | 11 | First, add `ic-cdk-bindgen` as a build dependency to `Cargo.toml`. 12 | 13 | ```bash 14 | cargo add --build ic-cdk-bindgen 15 | ``` 16 | 17 | Next, add the Candid interface file of the `callee` canister (e.g., at `candid/callee.did`). 18 | 19 | To generate Rust code from `callee.did`, we use `ic-cdk-bindgen` in the crate's `build.rs` build-script : 20 | 21 | ```rust,no_run 22 | // build.rs 23 | fn main() { 24 | let CALLEE_CANISTER_ID : candid::Principal = todo!(); 25 | ic_cdk_bindgen::Config::new("callee", "candid/callee.did") 26 | .static_callee(CALLEE_CANISTER_ID) 27 | .generate(); 28 | } 29 | ``` 30 | 31 | Finally, in the canister source code (e.g. `lib.rs`, `main.rs`), include the generated code: 32 | 33 | ```rust,ignore 34 | #[allow(dead_code, unused_imports)] 35 | mod callee { 36 | include!(concat!(env!("OUT_DIR"), "/callee.rs")); 37 | } 38 | 39 | #[ic_cdk::update] 40 | async fn invoke_callee() { 41 | // In modern IDE/editors like VSCode, you can often use "Go to Definition" or similar features 42 | // to quickly navigate to the generated bindings. 43 | let _result = callee::some_method().await; 44 | } 45 | ``` 46 | 47 | ## Dynamic Callee Mode 48 | 49 | The example above demonstrates the "Static callee" mode, where the canister ID is known at compile time. 50 | In this mode, the generated code includes the hardcoded canister ID, making it suitable for deployments 51 | where the canister ID is fixed and known ahead of time. 52 | 53 | The ICP Environment Variables feature enables a workflow where the canister ID can be set via 54 | environment variables, allowing for more flexible deployments. The "Dynamic callee" mode is designed for this workflow. 55 | 56 | For example, the `icp-cli` CLI tool sets an Env Var if there is a canister named `callee` in the project: 57 | - name: `ICP_CANISTER_ID:callee` 58 | - value: The text representation of the Principal 59 | 60 | Just apply a one line change in the `build.rs` script: 61 | 62 | ```rust,no_run 63 | // build.rs 64 | fn main() { 65 | ic_cdk_bindgen::Config::new("callee", "candid/callee.did") 66 | .dynamic_callee("ICP_CANISTER_ID:callee") // <--- Change made here 67 | .generate(); 68 | } 69 | ``` 70 | 71 | Then the generated code will use the canister ID from the environment variable at runtime. 72 | 73 | ## Type Selector Config 74 | 75 | The `candid` project specifies a "Type Selector" configuration language that controls the details of Rust code generation. 76 | You can use type selectors to customize how Candid types are translated to Rust types. 77 | 78 | First, create a TOML file for your canister (e.g., `callee.toml`) with a `rust` table at the top level. For example: 79 | 80 | ```toml 81 | # callee.toml 82 | [rust] 83 | visibility = "pub(crate)" 84 | attributes = "#[derive(CandidType, Deserialize, Clone, Debug)]" 85 | blob.use_type = "Vec" 86 | change_details.variant.creation.name = "CreationRecord" 87 | ``` 88 | 89 | Then, add the type selector configuration to your build script using `set_type_selector_config`: 90 | 91 | ```rust,no_run 92 | // build.rs 93 | fn main() { 94 | ic_cdk_bindgen::Config::new("callee", "candid/callee.did") 95 | .set_type_selector_config("path/to/callee.toml") 96 | .dynamic_callee("ICP_CANISTER_ID:callee") 97 | .generate(); 98 | } 99 | ``` 100 | 101 | The updated bindings can be seen by using the "Go to definition" or similar features from your IDE/editors. 102 | 103 | For more details on type selector syntax and capabilities, refer to the [specification](https://github.com/dfinity/candid/blob/master/spec/Type-selector.md#rust-binding-configuration). 104 | 105 | 106 | ## Use with `dfx` 107 | 108 | The previous version of `ic-cdk-bindgen` was designed to only work with `dfx` CLI tool. 109 | It relied on the `dfx` to set up some environment variables (note: this is the general UNIX environment variable), 110 | so that bindgen could learn the path to the Candid file and the canister ID at build time. 111 | Such `dfx` specific handling is removed in the updated version of `ic-cdk-bindgen`. 112 | 113 | To use the updated `ic-cdk-bindgen` with `dfx`, it is recommended to handle the `dfx` environment variables manually in your `build.rs` script. 114 | 115 | ```rust,no_run 116 | // build.rs 117 | fn main() { 118 | let name = "callee"; 119 | let normalized_name = name.replace('-', "_").to_uppercase(); 120 | let canister_id_var_name = format!("CANISTER_ID_{}", normalized_name); 121 | let canister_id_str = std::env::var(&canister_id_var_name).unwrap(); 122 | let canister_id = candid::Principal::from_text(&canister_id_str).unwrap(); 123 | let candid_path_var_name = format!("CANISTER_CANDID_PATH_{}", normalized_name); 124 | let candid_path = std::env::var(&candid_path_var_name).unwrap(); 125 | ic_cdk_bindgen::Config::new(name, candid_path) 126 | .static_callee(canister_id) 127 | .generate(); 128 | } 129 | ``` 130 | -------------------------------------------------------------------------------- /e2e-tests/src/bin/timers.rs: -------------------------------------------------------------------------------- 1 | use futures::{stream::FuturesUnordered, StreamExt}; 2 | use ic_cdk::{ 3 | api::canister_self, 4 | call::Call, 5 | futures::spawn, 6 | management_canister::{HttpMethod, HttpRequestArgs}, 7 | query, update, 8 | }; 9 | use ic_cdk_timers::{clear_timer, set_timer, set_timer_interval, TimerId}; 10 | use std::{ 11 | cell::{Cell, RefCell}, 12 | sync::atomic::{AtomicU32, Ordering}, 13 | time::Duration, 14 | }; 15 | 16 | thread_local! { 17 | static EVENTS: RefCell> = RefCell::default(); 18 | static LONG: Cell = Cell::default(); 19 | static REPEATING: Cell = Cell::default(); 20 | } 21 | 22 | static EXECUTED_TIMERS: AtomicU32 = AtomicU32::new(0); 23 | 24 | #[query] 25 | fn get_events() -> Vec { 26 | EVENTS.with(|events| events.borrow().clone()) 27 | } 28 | 29 | #[update] 30 | fn clear_events() { 31 | EVENTS.with(|events| events.borrow_mut().clear()); 32 | } 33 | 34 | #[update] 35 | fn schedule() { 36 | set_timer(Duration::from_secs(2), async { 37 | add_event("2"); 38 | }); 39 | set_timer(Duration::from_secs(1), async { 40 | add_event("1"); 41 | set_timer(Duration::from_secs(2), async { add_event("3") }); 42 | }); 43 | set_timer(Duration::from_secs(4), async { 44 | add_event("4"); 45 | }); 46 | } 47 | 48 | #[update] 49 | fn schedule_n_timers(n: u32) { 50 | for i in 0..n { 51 | ic_cdk_timers::set_timer(Duration::from_nanos(i.into()), async move { 52 | EXECUTED_TIMERS.fetch_add(1, Ordering::Relaxed); 53 | }); 54 | } 55 | } 56 | 57 | #[query] 58 | fn executed_timers() -> u32 { 59 | EXECUTED_TIMERS.load(Ordering::Relaxed) 60 | } 61 | 62 | #[update] 63 | fn schedule_long() { 64 | let id = set_timer(Duration::from_secs(9), async { add_event("long") }); 65 | LONG.with(|long| long.set(id)); 66 | } 67 | 68 | #[update] 69 | fn set_self_cancelling_timer() { 70 | let id = set_timer(Duration::from_secs(0), async { 71 | cancel_long(); 72 | add_event("timer cancelled self"); 73 | }); 74 | LONG.with(|long| long.set(id)); 75 | } 76 | 77 | #[update] 78 | fn cancel_long() { 79 | LONG.with(|long| clear_timer(long.get())); 80 | } 81 | 82 | #[update] 83 | fn start_repeating() { 84 | let id = set_timer_interval(Duration::from_secs(1), async || { 85 | add_event("repeat"); 86 | }); 87 | REPEATING.with(|repeating| repeating.set(id)); 88 | } 89 | 90 | #[update] 91 | fn start_repeating_async() { 92 | let id = set_timer_interval(Duration::from_secs(1), async || { 93 | Call::bounded_wait(canister_self(), "add_event_method") 94 | .with_arg("repeat") 95 | .await 96 | .unwrap(); 97 | }); 98 | REPEATING.with(|repeating| repeating.set(id)); 99 | } 100 | 101 | #[update] 102 | fn start_repeating_serial() { 103 | let id = ic_cdk_timers::set_timer_interval_serial(Duration::from_secs(1), async || { 104 | Call::bounded_wait(canister_self(), "add_event_method") 105 | .with_arg("repeat serial") 106 | .await 107 | .unwrap(); 108 | // best way of sleeping is a mocked http outcall 109 | ic_cdk::management_canister::http_request_with_closure( 110 | &HttpRequestArgs { 111 | url: "http://mock".to_string(), 112 | method: HttpMethod::GET, 113 | headers: vec![], 114 | body: None, 115 | max_response_bytes: None, 116 | transform: None, 117 | is_replicated: None, 118 | }, 119 | |resp| resp, 120 | ) 121 | .await 122 | .unwrap(); 123 | }); 124 | REPEATING.with(|repeating| repeating.set(id)); 125 | } 126 | 127 | #[update] 128 | fn set_self_cancelling_periodic_timer() { 129 | let id = set_timer_interval(Duration::from_secs(1), async || { 130 | stop_repeating(); 131 | add_event("periodic timer cancelled self"); 132 | }); 133 | REPEATING.with(|repeating| repeating.set(id)); 134 | } 135 | 136 | #[update] 137 | fn stop_repeating() { 138 | REPEATING.with(|repeating| clear_timer(repeating.get())); 139 | } 140 | 141 | fn add_event(event: &str) { 142 | EVENTS.with(|events| events.borrow_mut().push(event.to_string())); 143 | } 144 | 145 | #[update] 146 | fn global_timer_set(timestamp: u64) -> u64 { 147 | ic_cdk::api::global_timer_set(timestamp) 148 | } 149 | 150 | #[update] 151 | fn add_event_method(name: &str) { 152 | add_event(&format!("method {name}")); 153 | } 154 | 155 | #[update] 156 | fn async_await() { 157 | set_timer(Duration::from_secs(1), async { 158 | add_event("1"); 159 | Call::bounded_wait(canister_self(), "add_event_method") 160 | .with_arg("outer") 161 | .await 162 | .unwrap(); 163 | add_event("2"); 164 | spawn(async { 165 | Call::bounded_wait(canister_self(), "add_event_method") 166 | .with_arg("spawned") 167 | .await 168 | .unwrap(); 169 | }); 170 | let futs = FuturesUnordered::new(); 171 | for _ in 0..3 { 172 | futs.push(async move { 173 | Call::bounded_wait(canister_self(), "add_event_method") 174 | .with_arg("concurrent") 175 | .await 176 | .unwrap(); 177 | }); 178 | } 179 | futs.collect::<()>().await; 180 | }); 181 | add_event("0") 182 | } 183 | 184 | fn main() {} 185 | -------------------------------------------------------------------------------- /ic-cdk-timers/src/timer_executor.rs: -------------------------------------------------------------------------------- 1 | use std::{mem, time::Duration}; 2 | 3 | use slotmap::KeyData; 4 | 5 | use crate::state::{SerialClosure, TASKS, Task, TaskId}; 6 | 7 | #[cfg_attr( 8 | target_family = "wasm", 9 | unsafe(export_name = "canister_update timer_executor") 10 | )] 11 | #[cfg_attr( 12 | not(target_family = "wasm"), 13 | unsafe(export_name = "canister_update_ic_cdk_internal.timer_executor") 14 | )] 15 | extern "C" fn timer_executor() { 16 | ic_cdk_executor::in_tracking_executor_context(|| { 17 | let mut caller = [0; 32]; 18 | let caller = { 19 | let sz = ic0::msg_caller_size(); 20 | ic0::msg_caller_copy(&mut caller[..sz], 0); 21 | &caller[..sz] 22 | }; 23 | let mut canister_self = [0; 32]; 24 | let canister_self = { 25 | let sz = ic0::canister_self_size(); 26 | ic0::canister_self_copy(&mut canister_self[..sz], 0); 27 | &canister_self[..sz] 28 | }; 29 | 30 | if caller != canister_self { 31 | ic0::trap(b"This function is internal to ic-cdk and should not be called externally."); 32 | } 33 | 34 | // timer_executor is only called by the canister itself (from global_timer), 35 | // so we can safely assume that the argument is a valid TimerId (u64). 36 | // And we don't need decode_one_with_config/DecoderConfig to defend against a malicious payload. 37 | assert!(ic0::msg_arg_data_size() == 8); 38 | let mut arg_bytes = [0; 8]; 39 | ic0::msg_arg_data_copy(&mut arg_bytes, 0); 40 | let task_id = u64::from_be_bytes(arg_bytes); 41 | let task_id = TaskId::from(KeyData::from_ffi(task_id)); 42 | 43 | // We can't be holding `TASKS` when we call the function, because it may want to schedule more tasks. 44 | // Instead, we swap the task out in order to call it, and then either swap it back in, or remove it. 45 | let task = TASKS.with_borrow_mut(|tasks| { 46 | if let Some(task) = tasks.get_mut(task_id) { 47 | // Replace with Invalid to take ownership. 48 | // The Invalid variant should not last past the end of this function. 49 | Some(mem::replace(task, Task::Invalid)) 50 | } else { 51 | None 52 | } 53 | }); 54 | if let Some(task) = task { 55 | // Each branch should: 56 | // - remove the Invalid task state OR panic, before any awaits 57 | // - call msg_reply OR panic when done 58 | match task { 59 | Task::Once(fut) => { 60 | ic_cdk_executor::spawn_protected(async move { 61 | fut.await; 62 | ic0::msg_reply(); 63 | }); 64 | // Invalid cleared in the same round 65 | TASKS.with_borrow_mut(|tasks| tasks.remove(task_id)); 66 | } 67 | Task::Repeated { 68 | mut func, 69 | interval, 70 | concurrent_calls, 71 | } => { 72 | let invocation = func(); 73 | // Invalid cleared in the same round 74 | TASKS.with_borrow_mut(|tasks| { 75 | tasks[task_id] = Task::Repeated { 76 | func, 77 | interval, 78 | concurrent_calls, 79 | }; 80 | }); 81 | ic_cdk_executor::spawn_protected(async move { 82 | invocation.await; 83 | ic0::msg_reply(); 84 | }); 85 | } 86 | Task::RepeatedSerial { func, interval } => { 87 | // Invalid cleared in the same round 88 | TASKS.with_borrow_mut(|tasks| { 89 | tasks[task_id] = Task::RepeatedSerialBusy { interval }; 90 | }); 91 | ic_cdk_executor::spawn_protected(async move { 92 | // Option for `take` in Drop; always Some 93 | struct ReplaceGuard(Option>, Duration, TaskId); 94 | impl Drop for ReplaceGuard { 95 | fn drop(&mut self) { 96 | let func = self.0.take().unwrap(); 97 | let interval = self.1; 98 | let task_id = self.2; 99 | TASKS.with_borrow_mut(|tasks| { 100 | tasks[task_id] = Task::RepeatedSerial { func, interval }; 101 | }); 102 | } 103 | } 104 | let mut guard = ReplaceGuard(Some(func), interval, task_id); 105 | guard.0.as_mut().unwrap().call().await; 106 | ic0::msg_reply(); 107 | }); 108 | } 109 | Task::RepeatedSerialBusy { .. } => { 110 | // Invalid cleared in the same round 111 | TASKS.with_borrow_mut(|tasks| { 112 | tasks[task_id] = task; 113 | }); 114 | ic0::msg_reply(); 115 | } 116 | Task::Invalid => { 117 | // Invalid impossible 118 | unreachable!( 119 | "[ic-cdk-timers] internal error: invalid task state in executor method" 120 | ) 121 | } 122 | } 123 | } else { 124 | ic0::msg_reply(); 125 | } 126 | }); 127 | } 128 | -------------------------------------------------------------------------------- /e2e-tests/tests/test_utilities.rs: -------------------------------------------------------------------------------- 1 | use candid::utils::{ArgumentDecoder, ArgumentEncoder}; 2 | use candid::Principal; 3 | use cargo_metadata::MetadataCommand; 4 | use pocket_ic::common::rest::RawEffectivePrincipal; 5 | use pocket_ic::{call_candid, PocketIc, PocketIcBuilder, RejectResponse}; 6 | use std::path::PathBuf; 7 | use std::process::{Command, Stdio}; 8 | use std::sync::Once; 9 | 10 | /// Builds a canister with the specified name from the current 11 | /// package and returns the WebAssembly module. 12 | pub fn cargo_build_canister(bin_name: &str) -> Vec { 13 | static LOG_INIT: Once = Once::new(); 14 | LOG_INIT.call_once(env_logger::init); 15 | let dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); 16 | 17 | let cargo_toml_path = dir.join("Cargo.toml"); 18 | 19 | let target_dir = MetadataCommand::new() 20 | .manifest_path(&cargo_toml_path) 21 | .no_deps() 22 | .exec() 23 | .expect("failed to run cargo metadata") 24 | .target_directory; 25 | 26 | // We use a different target path to stop the native cargo build 27 | // cache being invalidated every time we run this function 28 | let wasm_target_dir = target_dir.join("canister-build"); 29 | 30 | let mut cmd = Command::new("cargo"); 31 | let target = match std::env::var("WASM64") { 32 | Ok(_) => { 33 | cmd.args([ 34 | "+nightly-2025-05-09", // 1.88.0 35 | "build", 36 | "-Z", 37 | "build-std=std,panic_abort", 38 | "--target", 39 | "wasm64-unknown-unknown", 40 | ]); 41 | "wasm64-unknown-unknown" 42 | } 43 | Err(_) => { 44 | cmd.args(["build", "--target", "wasm32-unknown-unknown"]); 45 | "wasm32-unknown-unknown" 46 | } 47 | }; 48 | 49 | let cmd = cmd 50 | .args([ 51 | "--bin", 52 | bin_name, 53 | "--profile", 54 | "canister-release", 55 | "--manifest-path", 56 | &cargo_toml_path.to_string_lossy(), 57 | "--target-dir", 58 | wasm_target_dir.as_ref(), 59 | ]) 60 | .stdout(Stdio::inherit()) 61 | .stderr(Stdio::inherit()); 62 | 63 | let output = cmd.output().expect("failed to locate cargo"); 64 | assert!(output.status.success(), "failed to compile the wasm binary"); 65 | 66 | let wasm_path = wasm_target_dir 67 | .join(target) 68 | .join("canister-release") 69 | .join(bin_name) 70 | .with_extension("wasm"); 71 | 72 | std::fs::read(&wasm_path).unwrap_or_else(|e| { 73 | panic!( 74 | "failed to read compiled Wasm file from {:?}: {}", 75 | &wasm_path, e 76 | ) 77 | }) 78 | } 79 | 80 | // The linter complains "function `update` is never used" 81 | // because not EVERY test uses this function. 82 | pub fn update( 83 | env: &PocketIc, 84 | canister_id: Principal, 85 | method: &str, 86 | input: Input, 87 | ) -> Result 88 | where 89 | Input: ArgumentEncoder, 90 | Output: for<'a> ArgumentDecoder<'a>, 91 | { 92 | call_candid(env, canister_id, RawEffectivePrincipal::None, method, input) 93 | } 94 | 95 | /// Creates a PocketIcBuilder with the base configuration for e2e tests. 96 | /// 97 | /// The PocketIc server binary is cached for reuse. 98 | pub fn pic_base() -> PocketIcBuilder { 99 | let pocket_ic_server = check_pocket_ic_server(); 100 | PocketIcBuilder::new() 101 | .with_server_binary(pocket_ic_server) 102 | .with_application_subnet() 103 | } 104 | 105 | fn check_pocket_ic_server() -> PathBuf { 106 | let dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); 107 | let cargo_toml_path = dir.join("Cargo.toml"); 108 | let metadata = MetadataCommand::new() 109 | .manifest_path(&cargo_toml_path) 110 | .exec() 111 | .expect("failed to run cargo metadata"); 112 | let e2e_tests_package = metadata 113 | .packages 114 | .iter() 115 | .find(|m| m.name.as_ref() == "ic-cdk-e2e-tests") 116 | .expect("ic-cdk-e2e-tests not found in Cargo.toml"); 117 | let pocket_ic_tag = e2e_tests_package 118 | .dependencies 119 | .iter() 120 | .find(|d| d.name == "pocket-ic") 121 | .expect("pocket-ic not found in Cargo.toml") 122 | .source 123 | .as_ref() 124 | .expect("pocket-ic source not found in Cargo.toml") 125 | .repr 126 | .split_once("tag=") 127 | .expect("`tag=` not found in pocket-ic source") 128 | .1; 129 | let target_dir = metadata.target_directory; 130 | let artifact_dir = target_dir.join("e2e-tests-artifacts"); 131 | let tag_path = artifact_dir.join("pocket-ic-tag"); 132 | let server_binary_path = artifact_dir.join("pocket-ic"); 133 | if let Ok(tag) = std::fs::read_to_string(&tag_path) { 134 | if tag == pocket_ic_tag && server_binary_path.exists() { 135 | return server_binary_path.into(); 136 | } 137 | } 138 | panic!("pocket-ic server not found or tag mismatch, please run `scripts/download_pocket_ic_server.sh` in the project root"); 139 | } 140 | 141 | #[cfg(test)] 142 | mod tests { 143 | use super::*; 144 | 145 | #[test] 146 | fn test_pocket_ic() { 147 | let _pic = pic_base(); 148 | } 149 | 150 | #[test] 151 | fn test_update() { 152 | let pic = pic_base().build(); 153 | let canister_id = pic.create_canister(); 154 | pic.add_cycles(canister_id, 2_000_000_000_000); 155 | pic.install_canister( 156 | canister_id, 157 | b"\x00asm\x01\x00\x00\x00".to_vec(), 158 | vec![], 159 | None, 160 | ); 161 | assert!(update::<(), ()>(&pic, canister_id, "insert", ()).is_err()); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /ic-cdk/src/api/management_canister/bitcoin/mod.rs: -------------------------------------------------------------------------------- 1 | //! The IC Bitcoin API. 2 | //! 3 | //! Check [Bitcoin integration](https://internetcomputer.org/docs/current/developer-docs/integrations/bitcoin/bitcoin-how-it-works/#api) for more details. 4 | 5 | use crate::api::call::{CallResult, call_with_payment128}; 6 | use candid::Principal; 7 | 8 | mod types; 9 | pub use types::*; 10 | 11 | const GET_UTXO_MAINNET: u128 = 10_000_000_000; 12 | const GET_UTXO_TESTNET: u128 = 4_000_000_000; 13 | 14 | const GET_CURRENT_FEE_PERCENTILES_MAINNET: u128 = 100_000_000; 15 | const GET_CURRENT_FEE_PERCENTILES_TESTNET: u128 = 40_000_000; 16 | 17 | const GET_BALANCE_MAINNET: u128 = 100_000_000; 18 | const GET_BALANCE_TESTNET: u128 = 40_000_000; 19 | 20 | const SEND_TRANSACTION_SUBMISSION_MAINNET: u128 = 5_000_000_000; 21 | const SEND_TRANSACTION_SUBMISSION_TESTNET: u128 = 2_000_000_000; 22 | 23 | const SEND_TRANSACTION_PAYLOAD_MAINNET: u128 = 20_000_000; 24 | const SEND_TRANSACTION_PAYLOAD_TESTNET: u128 = 8_000_000; 25 | 26 | const GET_BLOCK_HEADERS_MAINNET: u128 = 4_000_000_000; 27 | const GET_BLOCK_HEADERS_TESTNET: u128 = 4_000_000_000; 28 | 29 | /// See [IC method `bitcoin_get_balance`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-bitcoin_get_balance). 30 | /// 31 | /// This call requires cycles payment. 32 | /// This method handles the cycles cost under the hood. 33 | /// Check [API fees & Pricing](https://internetcomputer.org/docs/current/developer-docs/integrations/bitcoin/bitcoin-how-it-works/#api-fees--pricing) for more details. 34 | pub async fn bitcoin_get_balance(arg: GetBalanceRequest) -> CallResult<(Satoshi,)> { 35 | let cycles = match arg.network { 36 | BitcoinNetwork::Mainnet => GET_BALANCE_MAINNET, 37 | BitcoinNetwork::Testnet => GET_BALANCE_TESTNET, 38 | BitcoinNetwork::Regtest => 0, 39 | }; 40 | call_with_payment128( 41 | Principal::management_canister(), 42 | "bitcoin_get_balance", 43 | (arg,), 44 | cycles, 45 | ) 46 | .await 47 | } 48 | 49 | /// See [IC method `bitcoin_get_utxos`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-bitcoin_get_utxos). 50 | /// 51 | /// This call requires cycles payment. 52 | /// This method handles the cycles cost under the hood. 53 | /// Check [API fees & Pricing](https://internetcomputer.org/docs/current/developer-docs/integrations/bitcoin/bitcoin-how-it-works/#api-fees--pricing) for more details. 54 | pub async fn bitcoin_get_utxos(arg: GetUtxosRequest) -> CallResult<(GetUtxosResponse,)> { 55 | let cycles = match arg.network { 56 | BitcoinNetwork::Mainnet => GET_UTXO_MAINNET, 57 | BitcoinNetwork::Testnet => GET_UTXO_TESTNET, 58 | BitcoinNetwork::Regtest => 0, 59 | }; 60 | call_with_payment128( 61 | Principal::management_canister(), 62 | "bitcoin_get_utxos", 63 | (arg,), 64 | cycles, 65 | ) 66 | .await 67 | } 68 | 69 | fn send_transaction_fee(arg: &SendTransactionRequest) -> u128 { 70 | let (submission, payload) = match arg.network { 71 | BitcoinNetwork::Mainnet => ( 72 | SEND_TRANSACTION_SUBMISSION_MAINNET, 73 | SEND_TRANSACTION_PAYLOAD_MAINNET, 74 | ), 75 | BitcoinNetwork::Testnet => ( 76 | SEND_TRANSACTION_SUBMISSION_TESTNET, 77 | SEND_TRANSACTION_PAYLOAD_TESTNET, 78 | ), 79 | BitcoinNetwork::Regtest => (0, 0), 80 | }; 81 | submission + payload * arg.transaction.len() as u128 82 | } 83 | 84 | /// See [IC method `bitcoin_send_transaction`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-bitcoin_send_transaction). 85 | /// 86 | /// This call requires cycles payment. 87 | /// This method handles the cycles cost under the hood. 88 | /// Check [API fees & Pricing](https://internetcomputer.org/docs/current/developer-docs/integrations/bitcoin/bitcoin-how-it-works/#api-fees--pricing) for more details. 89 | pub async fn bitcoin_send_transaction(arg: SendTransactionRequest) -> CallResult<()> { 90 | let cycles = send_transaction_fee(&arg); 91 | call_with_payment128( 92 | Principal::management_canister(), 93 | "bitcoin_send_transaction", 94 | (arg,), 95 | cycles, 96 | ) 97 | .await 98 | } 99 | 100 | /// See [IC method `bitcoin_get_current_fee_percentiles`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-bitcoin_get_current_fee_percentiles). 101 | /// 102 | /// This call requires cycles payment. 103 | /// This method handles the cycles cost under the hood. 104 | /// Check [API fees & Pricing](https://internetcomputer.org/docs/current/developer-docs/integrations/bitcoin/bitcoin-how-it-works/#api-fees--pricing) for more details. 105 | pub async fn bitcoin_get_current_fee_percentiles( 106 | arg: GetCurrentFeePercentilesRequest, 107 | ) -> CallResult<(Vec,)> { 108 | let cycles = match arg.network { 109 | BitcoinNetwork::Mainnet => GET_CURRENT_FEE_PERCENTILES_MAINNET, 110 | BitcoinNetwork::Testnet => GET_CURRENT_FEE_PERCENTILES_TESTNET, 111 | BitcoinNetwork::Regtest => 0, 112 | }; 113 | call_with_payment128( 114 | Principal::management_canister(), 115 | "bitcoin_get_current_fee_percentiles", 116 | (arg,), 117 | cycles, 118 | ) 119 | .await 120 | } 121 | 122 | /// See [IC method `bitcoin_get_block_headers`](https://internetcomputer.org/docs/current/references/ic-interface-spec#ic-bitcoin_get_block_headers). 123 | /// 124 | /// This call requires cycles payment. 125 | /// This method handles the cycles cost under the hood. 126 | /// Check [API fees & Pricing](https://internetcomputer.org/docs/current/developer-docs/integrations/bitcoin/bitcoin-how-it-works/#api-fees--pricing) for more details. 127 | pub async fn bitcoin_get_block_headers( 128 | arg: GetBlockHeadersRequest, 129 | ) -> CallResult<(GetBlockHeadersResponse,)> { 130 | let cycles = match arg.network { 131 | BitcoinNetwork::Mainnet => GET_BLOCK_HEADERS_MAINNET, 132 | BitcoinNetwork::Testnet => GET_BLOCK_HEADERS_TESTNET, 133 | BitcoinNetwork::Regtest => 0, 134 | }; 135 | call_with_payment128( 136 | Principal::management_canister(), 137 | "bitcoin_get_block_headers", 138 | (arg,), 139 | cycles, 140 | ) 141 | .await 142 | } 143 | -------------------------------------------------------------------------------- /ic-cdk/src/api/management_canister/bitcoin/types.rs: -------------------------------------------------------------------------------- 1 | use candid::CandidType; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | /// 10^8 Satoshi = 1 Bitcoin. 5 | pub type Satoshi = u64; 6 | 7 | /// Bitcoin Network. 8 | #[derive( 9 | CandidType, 10 | Serialize, 11 | Deserialize, 12 | Debug, 13 | PartialEq, 14 | Eq, 15 | PartialOrd, 16 | Ord, 17 | Hash, 18 | Clone, 19 | Copy, 20 | Default, 21 | )] 22 | pub enum BitcoinNetwork { 23 | /// Mainnet. 24 | #[serde(rename = "mainnet")] 25 | Mainnet, 26 | /// Testnet. 27 | #[serde(rename = "testnet")] 28 | Testnet, 29 | /// Regtest. 30 | /// 31 | /// This is only available when developing with local replica. 32 | #[serde(rename = "regtest")] 33 | #[default] 34 | Regtest, 35 | } 36 | 37 | /// Bitcoin Address. 38 | pub type BitcoinAddress = String; 39 | 40 | /// Block Hash. 41 | pub type BlockHash = Vec; 42 | 43 | /// Element in the Response of [`bitcoin_get_current_fee_percentiles`](super::bitcoin_get_current_fee_percentiles). 44 | pub type MillisatoshiPerByte = u64; 45 | 46 | /// Identifier of [`Utxo`]. 47 | #[derive( 48 | CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, 49 | )] 50 | pub struct Outpoint { 51 | /// Transaction Identifier. 52 | pub txid: Vec, 53 | /// A implicit index number. 54 | pub vout: u32, 55 | } 56 | 57 | /// Unspent transaction output (UTXO). 58 | #[derive( 59 | CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, 60 | )] 61 | pub struct Utxo { 62 | /// See [`Outpoint`]. 63 | pub outpoint: Outpoint, 64 | /// Value in the units of satoshi. 65 | pub value: Satoshi, 66 | /// Height in the chain. 67 | pub height: u32, 68 | } 69 | 70 | /// Filter for requesting UTXOs. 71 | #[derive( 72 | CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, 73 | )] 74 | pub enum UtxoFilter { 75 | /// Minimum number of confirmations. There is an upper bound of 144. Typically set to a value around 6 in practice. 76 | #[serde(rename = "min_confirmations")] 77 | MinConfirmations(u32), 78 | /// Page reference. 79 | /// 80 | /// DON'T construct it from scratch. 81 | /// Only get it from the `next_page` field of [`GetUtxosResponse`]. 82 | #[serde(rename = "page")] 83 | Page(Vec), 84 | } 85 | 86 | /// Argument type of [`bitcoin_get_balance`](super::bitcoin_get_balance). 87 | #[derive( 88 | CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, 89 | )] 90 | pub struct GetBalanceRequest { 91 | /// See [`BitcoinAddress`]. 92 | pub address: BitcoinAddress, 93 | /// See [`BitcoinNetwork`]. 94 | pub network: BitcoinNetwork, 95 | /// Minimum number of confirmations. There is an upper bound of 144. Typically set to a value around 6 in practice. 96 | pub min_confirmations: Option, 97 | } 98 | 99 | /// Argument type of [`bitcoin_get_utxos`](super::bitcoin_get_utxos). 100 | #[derive( 101 | CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, 102 | )] 103 | pub struct GetUtxosRequest { 104 | /// See [`BitcoinAddress`]. 105 | pub address: BitcoinAddress, 106 | /// See [`BitcoinNetwork`]. 107 | pub network: BitcoinNetwork, 108 | /// See [`UtxoFilter`]. 109 | pub filter: Option, 110 | } 111 | 112 | /// Response type of [`bitcoin_get_utxos`](super::bitcoin_get_utxos). 113 | #[derive( 114 | CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, 115 | )] 116 | pub struct GetUtxosResponse { 117 | /// List of UTXOs. 118 | pub utxos: Vec, 119 | /// Hash of the tip block. 120 | pub tip_block_hash: BlockHash, 121 | /// Height of the tip height. 122 | pub tip_height: u32, 123 | /// Page reference when the response needs to be paginated. 124 | /// 125 | /// To be used in [`UtxoFilter::Page`]. 126 | pub next_page: Option>, 127 | } 128 | 129 | /// Argument type of [`bitcoin_send_transaction`](super::bitcoin_send_transaction). 130 | #[derive( 131 | CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, 132 | )] 133 | pub struct SendTransactionRequest { 134 | /// The serialized transaction data. 135 | /// 136 | /// Several checks are performed. 137 | /// See [IC method `bitcoin_send_transaction`](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-bitcoin_send_transaction). 138 | pub transaction: Vec, 139 | /// See [`BitcoinNetwork`]. 140 | pub network: BitcoinNetwork, 141 | } 142 | 143 | /// Argument type of [`bitcoin_get_current_fee_percentiles`](super::bitcoin_get_current_fee_percentiles). 144 | #[derive( 145 | CandidType, 146 | Serialize, 147 | Deserialize, 148 | Debug, 149 | PartialEq, 150 | Eq, 151 | PartialOrd, 152 | Ord, 153 | Hash, 154 | Clone, 155 | Copy, 156 | Default, 157 | )] 158 | pub struct GetCurrentFeePercentilesRequest { 159 | /// See [`BitcoinNetwork`]. 160 | pub network: BitcoinNetwork, 161 | } 162 | 163 | /// Argument type of [`bitcoin_get_block_headers`](super::bitcoin_get_block_headers). 164 | #[derive( 165 | CandidType, 166 | Serialize, 167 | Deserialize, 168 | Debug, 169 | PartialEq, 170 | Eq, 171 | PartialOrd, 172 | Ord, 173 | Hash, 174 | Clone, 175 | Copy, 176 | Default, 177 | )] 178 | pub struct GetBlockHeadersRequest { 179 | /// The starting block height for the request. 180 | pub start_height: u32, 181 | /// The ending block height for the request, or `None` for the current tip. 182 | pub end_height: Option, 183 | /// See [`BitcoinNetwork`]. 184 | pub network: BitcoinNetwork, 185 | } 186 | 187 | /// Response type of [`bitcoin_get_block_headers`](super::bitcoin_get_block_headers). 188 | #[derive( 189 | CandidType, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, 190 | )] 191 | pub struct GetBlockHeadersResponse { 192 | /// The tip of the blockchain when this request was filled. 193 | pub tip_height: u32, 194 | /// The requested block headers. 195 | pub block_headers: Vec>, 196 | } 197 | -------------------------------------------------------------------------------- /ic-management-canister-types/tests/candid_equality.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused)] 2 | use candid::candid_method; 3 | use ic_management_canister_types::*; 4 | 5 | #[candid_method(update)] 6 | fn create_canister(_: CreateCanisterArgs) -> CreateCanisterResult { 7 | unimplemented!() 8 | } 9 | 10 | #[candid_method(update)] 11 | fn update_settings(_: UpdateSettingsArgs) { 12 | unimplemented!() 13 | } 14 | 15 | #[candid_method(update)] 16 | fn upload_chunk(_: UploadChunkArgs) -> UploadChunkResult { 17 | unimplemented!() 18 | } 19 | 20 | #[candid_method(update)] 21 | fn clear_chunk_store(_: ClearChunkStoreArgs) { 22 | unimplemented!() 23 | } 24 | 25 | #[candid_method(update)] 26 | fn stored_chunks(_: StoredChunksArgs) -> StoredChunksResult { 27 | unimplemented!() 28 | } 29 | 30 | #[candid_method(update)] 31 | fn install_code(_: InstallCodeArgs) { 32 | unimplemented!() 33 | } 34 | 35 | #[candid_method(update)] 36 | fn install_chunked_code(_: InstallChunkedCodeArgs) { 37 | unimplemented!() 38 | } 39 | 40 | #[candid_method(update)] 41 | fn uninstall_code(_: UninstallCodeArgs) { 42 | unimplemented!() 43 | } 44 | 45 | #[candid_method(update)] 46 | fn start_canister(_: StartCanisterArgs) { 47 | unimplemented!() 48 | } 49 | 50 | #[candid_method(update)] 51 | fn stop_canister(_: StopCanisterArgs) { 52 | unimplemented!() 53 | } 54 | 55 | #[candid_method(query)] 56 | fn canister_status(_: CanisterStatusArgs) -> CanisterStatusResult { 57 | unimplemented!() 58 | } 59 | 60 | #[candid_method(update)] 61 | fn canister_info(_: CanisterInfoArgs) -> CanisterInfoResult { 62 | unimplemented!() 63 | } 64 | 65 | #[candid_method(update)] 66 | fn canister_metadata(_: CanisterMetadataArgs) -> CanisterMetadataResult { 67 | unimplemented!() 68 | } 69 | 70 | #[candid_method(update)] 71 | fn subnet_info(_: SubnetInfoArgs) -> SubnetInfoResult { 72 | unimplemented!() 73 | } 74 | 75 | #[candid_method(update)] 76 | fn delete_canister(_: DeleteCanisterArgs) { 77 | unimplemented!() 78 | } 79 | 80 | #[candid_method(update)] 81 | fn deposit_cycles(_: DepositCyclesArgs) { 82 | unimplemented!() 83 | } 84 | 85 | #[candid_method(update)] 86 | fn raw_rand() -> RawRandResult { 87 | unimplemented!() 88 | } 89 | 90 | #[candid_method(update)] 91 | fn http_request(_: HttpRequestArgs) -> HttpRequestResult { 92 | unimplemented!() 93 | } 94 | 95 | #[candid_method(update)] 96 | fn ecdsa_public_key(_: EcdsaPublicKeyArgs) -> EcdsaPublicKeyResult { 97 | unimplemented!() 98 | } 99 | 100 | #[candid_method(update)] 101 | fn sign_with_ecdsa(_: SignWithEcdsaArgs) -> SignWithEcdsaResult { 102 | unimplemented!() 103 | } 104 | 105 | #[candid_method(update)] 106 | fn schnorr_public_key(_: SchnorrPublicKeyArgs) -> SchnorrPublicKeyResult { 107 | unimplemented!() 108 | } 109 | 110 | #[candid_method(update)] 111 | fn sign_with_schnorr(_: SignWithSchnorrArgs) -> SignWithSchnorrResult { 112 | unimplemented!() 113 | } 114 | 115 | #[candid_method(update)] 116 | fn vetkd_public_key(_: VetKDPublicKeyArgs) -> VetKDPublicKeyResult { 117 | unimplemented!() 118 | } 119 | 120 | #[candid_method(update)] 121 | fn vetkd_derive_key(_: VetKDDeriveKeyArgs) -> VetKDDeriveKeyResult { 122 | unimplemented!() 123 | } 124 | 125 | #[candid_method(update)] 126 | fn node_metrics_history(_: NodeMetricsHistoryArgs) -> NodeMetricsHistoryResult { 127 | unimplemented!() 128 | } 129 | 130 | #[candid_method(update)] 131 | fn provisional_create_canister_with_cycles( 132 | _: ProvisionalCreateCanisterWithCyclesArgs, 133 | ) -> ProvisionalCreateCanisterWithCyclesResult { 134 | unimplemented!() 135 | } 136 | 137 | #[candid_method(update)] 138 | fn provisional_top_up_canister(_: ProvisionalTopUpCanisterArgs) { 139 | unimplemented!() 140 | } 141 | 142 | #[candid_method(update)] 143 | fn take_canister_snapshot(_: TakeCanisterSnapshotArgs) -> TakeCanisterSnapshotResult { 144 | unimplemented!() 145 | } 146 | 147 | #[candid_method(update)] 148 | fn load_canister_snapshot(_: LoadCanisterSnapshotArgs) { 149 | unimplemented!() 150 | } 151 | 152 | #[candid_method(update)] 153 | fn list_canister_snapshots(_: ListCanisterSnapshotsArgs) -> ListCanisterSnapshotsResult { 154 | unimplemented!() 155 | } 156 | 157 | #[candid_method(update)] 158 | fn delete_canister_snapshot(_: DeleteCanisterSnapshotArgs) { 159 | unimplemented!() 160 | } 161 | 162 | #[candid_method(update)] 163 | fn read_canister_snapshot_metadata( 164 | _: ReadCanisterSnapshotMetadataArgs, 165 | ) -> ReadCanisterSnapshotMetadataResult { 166 | unimplemented!() 167 | } 168 | 169 | #[candid_method(update)] 170 | fn read_canister_snapshot_data(_: ReadCanisterSnapshotDataArgs) -> ReadCanisterSnapshotDataResult { 171 | unimplemented!() 172 | } 173 | 174 | #[candid_method(update)] 175 | fn upload_canister_snapshot_metadata( 176 | _: UploadCanisterSnapshotMetadataArgs, 177 | ) -> UploadCanisterSnapshotMetadataResult { 178 | unimplemented!() 179 | } 180 | 181 | #[candid_method(update)] 182 | fn upload_canister_snapshot_data(_: UploadCanisterSnapshotDataArgs) { 183 | unimplemented!() 184 | } 185 | 186 | #[candid_method(query)] 187 | fn fetch_canister_logs(_: FetchCanisterLogsArgs) -> FetchCanisterLogsResult { 188 | unimplemented!() 189 | } 190 | 191 | #[cfg(test)] 192 | mod test { 193 | use candid_parser::utils::{CandidSource, service_equal}; 194 | use ic_management_canister_types::*; 195 | 196 | #[test] 197 | fn candid_equality_test() { 198 | let declared_interface_str = 199 | std::fs::read_to_string("tests/ic.did").expect("Failed to read ic.did file"); 200 | let filtered_interface_str = declared_interface_str 201 | .lines() 202 | // Bitcoin APIs are deprecated from the management canister, so we filter them out. 203 | .filter(|line| !line.trim_start().starts_with("bitcoin_")) 204 | .collect::>() 205 | .join("\n"); 206 | let declared_interface = CandidSource::Text(&filtered_interface_str); 207 | 208 | candid::export_service!(); 209 | let implemented_interface_str = __export_service(); 210 | let implemented_interface = CandidSource::Text(&implemented_interface_str); 211 | 212 | let result = service_equal(declared_interface, implemented_interface); 213 | assert!(result.is_ok(), "{:?}", result.unwrap_err()); 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /e2e-tests/src/bin/macros/main.rs: -------------------------------------------------------------------------------- 1 | use ic_cdk::{export_candid, update}; 2 | use prost::Message; 3 | use std::marker::PhantomData; 4 | 5 | #[update(decode_with = "decode_arg0")] 6 | fn arg0() {} 7 | fn decode_arg0(_arg_bytes: Vec) {} 8 | 9 | #[update(decode_with = "decode_arg1")] 10 | fn arg1(a: u32) { 11 | assert_eq!(a, 1); 12 | } 13 | fn decode_arg1(arg_bytes: Vec) -> u32 { 14 | candid::utils::decode_one(&arg_bytes).unwrap() 15 | } 16 | 17 | #[update(decode_with = "decode_arg2")] 18 | fn arg2(a: u32, b: u32) { 19 | assert_eq!(a, 1); 20 | assert_eq!(b, 2); 21 | } 22 | fn decode_arg2(arg_bytes: Vec) -> (u32, u32) { 23 | candid::utils::decode_args(&arg_bytes).unwrap() 24 | } 25 | 26 | #[update(encode_with = "encode_ret0")] 27 | fn ret0() {} 28 | fn encode_ret0() -> Vec { 29 | vec![0] 30 | } 31 | 32 | #[update(encode_with = "encode_ret1")] 33 | fn ret1() -> u32 { 34 | 42 35 | } 36 | fn encode_ret1(v1: u32) -> Vec { 37 | vec![v1 as u8] 38 | } 39 | 40 | #[update(encode_with = "encode_ret2")] 41 | fn ret2() -> (u32, u32) { 42 | (1, 2) 43 | } 44 | fn encode_ret2(ret: (u32, u32)) -> Vec { 45 | vec![ret.0 as u8, ret.1 as u8] 46 | } 47 | 48 | // The type binding is generated by the build.rs script in the `e2e-tests` directory. 49 | mod canister { 50 | include!(concat!(env!("OUT_DIR"), "/canister.rs")); 51 | } 52 | use canister::*; 53 | 54 | /// The following two methods demonstrate how to use generic decode/encode functions. 55 | /// They take different types of arguments and return values. 56 | /// While the decode/encode functions are generic and can be used for both entry points. 57 | #[update(decode_with = "from_proto_bytes", encode_with = "to_proto_bytes")] 58 | fn method_one(arg: MethodOneRequest) -> MethodOneResponse { 59 | MethodOneResponse { 60 | result: arg.input.len() as i32, 61 | } 62 | } 63 | #[update(decode_with = "from_proto_bytes", encode_with = "to_proto_bytes")] 64 | fn method_two(arg: MethodTwoRequest) -> MethodTwoResponse { 65 | MethodTwoResponse { 66 | success: arg.values.iter().sum::() > 0.0, 67 | message: "Hello world!".to_string(), 68 | } 69 | } 70 | fn to_proto_bytes(msg: T) -> Vec { 71 | msg.encode_to_vec() 72 | } 73 | fn from_proto_bytes(msg: Vec) -> T { 74 | Message::decode(&msg[..]).unwrap() 75 | } 76 | 77 | /// The following method demonstrates how to specify `guard`/`decode_with`/`encode_with` attributes with generic parameters. 78 | #[update( 79 | guard = "generic_guard::<0>", // N = 0, any input length is accepted 80 | decode_with = "custom_candid_decode::<10000,_>", 81 | encode_with = "custom_candid_encode::<100,_>" 82 | )] 83 | fn generic(a: u32) -> u32 { 84 | a + 1 85 | } 86 | 87 | // A guard to verify the length of the input data is at least N bytes (N is specified as a const generic parameter). 88 | fn generic_guard() -> Result<(), String> { 89 | let input = ic_cdk::api::msg_arg_data(); 90 | if input.len() < N { 91 | Err("generic_guard failed".to_string()) 92 | } else { 93 | Ok(()) 94 | } 95 | } 96 | 97 | // A Candid decode function that uses a custom decoding quota N which is specified as a const generic parameter. 98 | fn custom_candid_decode candid::Deserialize<'a> + candid::CandidType>( 99 | bytes: Vec, 100 | ) -> T { 101 | let mut config = candid::de::DecoderConfig::new(); 102 | config.set_decoding_quota(N); 103 | candid::utils::decode_one_with_config(&bytes[..], &config).unwrap() 104 | } 105 | 106 | // A Candid encode function that checks the length of the encoded bytes is less than N which is specified as a const generic parameter. 107 | fn custom_candid_encode(v: T) -> Vec { 108 | let bytes = candid::utils::encode_one(v).unwrap(); 109 | assert!(bytes.len() < N); 110 | bytes 111 | } 112 | 113 | #[update(manual_reply = true)] 114 | fn manual_reply() -> PhantomData { 115 | let v: u32 = 1; 116 | let reply_bytes = candid::encode_one(v).unwrap(); 117 | ic_cdk::api::msg_reply(reply_bytes); 118 | PhantomData 119 | } 120 | 121 | #[update(guard = "guard1", guard = "guard2")] 122 | fn with_guards() {} 123 | 124 | fn guard1() -> Result<(), String> { 125 | let input = ic_cdk::api::msg_arg_data(); 126 | if !input[0].is_multiple_of(3) { 127 | Err("guard1 failed".to_string()) 128 | } else { 129 | Ok(()) 130 | } 131 | } 132 | 133 | fn guard2() -> Result<(), String> { 134 | let input = ic_cdk::api::msg_arg_data(); 135 | if !input[0].is_multiple_of(5) { 136 | Err("guard2 failed".to_string()) 137 | } else { 138 | Ok(()) 139 | } 140 | } 141 | 142 | // This method will be called with "malicious" payloads. 143 | // We expected that a default skipping_quota (10_000) is set in the update/query macros. 144 | // This will trigger a decoding error when the payload exceeds the quota. 145 | #[update] 146 | fn default_skipping_quota(_arg: Option) {} 147 | 148 | export_candid! {} 149 | 150 | fn main() { 151 | println!("{}", __export_service()); 152 | } 153 | 154 | #[cfg(test)] 155 | mod tests { 156 | use candid_parser::utils::{service_equal, CandidSource}; 157 | 158 | #[test] 159 | fn candid_equality_test() { 160 | // If `decode_with` is specified, the argument type would be `blob` in Candid. 161 | // If `encode_with` is specified, the return type would be `blob` in Candid. 162 | let expected = "service : { 163 | arg0 : (blob) -> (); 164 | arg1 : (blob) -> (); 165 | arg2 : (blob) -> (); 166 | ret0 : () -> (blob); 167 | ret1 : () -> (blob); 168 | ret2 : () -> (blob); 169 | method_one : (blob) -> (blob); 170 | method_two : (blob) -> (blob); 171 | generic : (blob) -> (blob); 172 | manual_reply : () -> (nat32); 173 | with_guards : () -> (); 174 | default_skipping_quota : (opt nat32) -> (); 175 | }"; 176 | let expected_candid = CandidSource::Text(expected); 177 | 178 | let actual = super::__export_service(); 179 | let actual_candid = CandidSource::Text(&actual); 180 | 181 | let result = service_equal(expected_candid, actual_candid); 182 | assert!(result.is_ok(), "{:?}", result.unwrap_err()); 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /ic0/ic0.txt: -------------------------------------------------------------------------------- 1 | ic0.msg_arg_data_size : () -> I; // I U RQ NRQ CQ Ry CRy F 2 | ic0.msg_arg_data_copy : (dst : I, offset : I, size : I) -> (); // I U RQ NRQ CQ Ry CRy F 3 | ic0.msg_caller_size : () -> I; // * 4 | ic0.msg_caller_copy : (dst : I, offset : I, size : I) -> (); // * 5 | ic0.msg_reject_code : () -> i32; // Ry Rt CRy CRt 6 | ic0.msg_reject_msg_size : () -> I ; // Rt CRt 7 | ic0.msg_reject_msg_copy : (dst : I, offset : I, size : I) -> (); // Rt CRt 8 | 9 | ic0.msg_deadline : () -> i64; // U Q CQ Ry Rt CRy CRt 10 | 11 | ic0.msg_reply_data_append : (src : I, size : I) -> (); // U RQ NRQ CQ Ry Rt CRy CRt 12 | ic0.msg_reply : () -> (); // U RQ NRQ CQ Ry Rt CRy CRt 13 | ic0.msg_reject : (src : I, size : I) -> (); // U RQ NRQ CQ Ry Rt CRy CRt 14 | 15 | ic0.msg_cycles_available128 : (dst : I) -> (); // U RQ Rt Ry 16 | ic0.msg_cycles_refunded128 : (dst : I) -> (); // Rt Ry 17 | ic0.msg_cycles_accept128 : (max_amount_high : i64, max_amount_low: i64, dst : I) 18 | -> (); // U RQ Rt Ry 19 | 20 | ic0.cycles_burn128 : (amount_high : i64, amount_low : i64, dst : I) 21 | -> (); // I G U RQ Ry Rt C T 22 | 23 | ic0.canister_self_size : () -> I; // * 24 | ic0.canister_self_copy : (dst : I, offset : I, size : I) -> (); // * 25 | ic0.canister_cycle_balance128 : (dst : I) -> (); // * 26 | ic0.canister_liquid_cycle_balance128 : (dst : I) -> (); // * 27 | ic0.canister_status : () -> i32; // * 28 | ic0.canister_version : () -> i64; // * 29 | 30 | ic0.subnet_self_size : () -> I; // * 31 | ic0.subnet_self_copy : (dst : I, offset : I, size : I) -> (); // * 32 | 33 | ic0.msg_method_name_size : () -> I; // F 34 | ic0.msg_method_name_copy : (dst : I, offset : I, size : I) -> (); // F 35 | ic0.accept_message : () -> (); // F 36 | 37 | ic0.call_new : 38 | ( callee_src : I, 39 | callee_size : I, 40 | name_src : I, 41 | name_size : I, 42 | reply_fun : I, 43 | reply_env : I, 44 | reject_fun : I, 45 | reject_env : I 46 | ) -> (); // U CQ Ry Rt CRy CRt T 47 | ic0.call_on_cleanup : (fun : I, env : I) -> (); // U CQ Ry Rt CRy CRt T 48 | ic0.call_data_append : (src : I, size : I) -> (); // U CQ Ry Rt CRy CRt T 49 | ic0.call_with_best_effort_response : (timeout_seconds : i32) -> (); // U CQ Ry Rt CRy CRt T 50 | ic0.call_cycles_add128 : (amount_high : i64, amount_low: i64) -> (); // U Ry Rt T 51 | ic0.call_perform : () -> ( err_code : i32 ); // U CQ Ry Rt CRy CRt T 52 | 53 | ic0.stable64_size : () -> (page_count : i64); // * s 54 | ic0.stable64_grow : (new_pages : i64) -> (old_page_count : i64); // * s 55 | ic0.stable64_write : (offset : i64, src : i64, size : i64) -> (); // * s 56 | ic0.stable64_read : (dst : i64, offset : i64, size : i64) -> (); // * s 57 | 58 | ic0.root_key_size : () -> I; // I G U RQ Ry Rt C T 59 | ic0.root_key_copy : (dst : I, offset : I, size : I) -> (); // I G U RQ Ry Rt C T 60 | ic0.certified_data_set : (src : I, size : I) -> (); // I G U Ry Rt T 61 | ic0.data_certificate_present : () -> i32; // * 62 | ic0.data_certificate_size : () -> I; // NRQ CQ 63 | ic0.data_certificate_copy : (dst : I, offset : I, size : I) -> (); // NRQ CQ 64 | 65 | ic0.time : () -> (timestamp : i64); // * 66 | ic0.global_timer_set : (timestamp : i64) -> i64; // I G U Ry Rt C T 67 | ic0.performance_counter : (counter_type : i32) -> (counter : i64); // * s 68 | ic0.is_controller : (src : I, size : I) -> ( result : i32); // * s 69 | ic0.in_replicated_execution : () -> (result : i32); // * s 70 | 71 | ic0.cost_call : (method_name_size: i64, payload_size : i64, dst : I) -> (); // * s 72 | ic0.cost_create_canister : (dst : I) -> (); // * s 73 | ic0.cost_http_request : (request_size : i64, max_res_bytes : i64, dst : I) -> (); // * s 74 | ic0.cost_sign_with_ecdsa : (src : I, size : I, ecdsa_curve: i32, dst : I) -> i32; // * s 75 | ic0.cost_sign_with_schnorr : (src : I, size : I, algorithm: i32, dst : I) -> i32; // * s 76 | ic0.cost_vetkd_derive_key : (src : I, size : I, vetkd_curve: i32, dst : I) -> i32; // * s 77 | 78 | ic0.env_var_count : () -> I; // * 79 | 80 | ic0.env_var_name_size : (index: I) -> I; // * 81 | ic0.env_var_name_copy : (index: I, dst: I, offset: I, size: I) -> (); // * 82 | ic0.env_var_name_exists : (name_src: I, name_size: I) -> i32; // * 83 | 84 | ic0.env_var_value_size : (name_src: I, name_size: I) -> I; // * 85 | ic0.env_var_value_copy : (name_src: I, name_size: I, dst: I, offset: I, size: I) -> (); // * 86 | 87 | ic0.debug_print : (src : I, size : I) -> (); // * s 88 | ic0.trap : (src : I, size : I) -> (); // * s 89 | --------------------------------------------------------------------------------