diff --git a/README.md b/README.md index 78be1eb..29221bf 100644 --- a/README.md +++ b/README.md @@ -18,11 +18,12 @@ icp new --subfolder | Template | Description | | --- | --- | +| [hello-world](./hello-world/) | Full-stack dapp with a frontend and backend canister (Rust or Motoko) | | [motoko](./motoko/) | A basic Motoko canister | | [rust](./rust/) | A basic Rust canister | -| [hello-world](./hello-world/) | Full-stack dapp with a frontend and backend canister (Rust or Motoko) | -| [static-website](./static-website/) | A static website deployed to an asset canister | +| [bitcoin-starter](./bitcoin-starter/) | Bitcoin integration with balance reading (Rust or Motoko) | | [proxy](./proxy/) | A pre-built proxy canister for use with `icp canister call --proxy` | +| [static-website](./static-website/) | A static website deployed to an asset canister | ## Contributing diff --git a/bitcoin-starter/.gitignore b/bitcoin-starter/.gitignore new file mode 100644 index 0000000..ede6f2b --- /dev/null +++ b/bitcoin-starter/.gitignore @@ -0,0 +1,17 @@ +# Various IDEs and Editors +.vscode/ +.idea/ +**/*~ + +# Mac OSX temporary files +.DS_Store +**/.DS_Store + +# environment variables +.env + +# rust +target/ + +# icp-cli +.icp/cache/ diff --git a/bitcoin-starter/README.md b/bitcoin-starter/README.md new file mode 100644 index 0000000..e5b94fd --- /dev/null +++ b/bitcoin-starter/README.md @@ -0,0 +1,114 @@ +# Bitcoin Starter Template + +Demonstrates reading Bitcoin balance from a canister on the Internet Computer using the [Bitcoin canister API](https://github.com/dfinity/bitcoin-canister/blob/master/INTERFACE_SPECIFICATION.md). + +## Bitcoin Canister IDs + +| IC Network | Bitcoin Network | Bitcoin Canister ID | +|------------|----------------|---------------------| +| Local (PocketIC) | regtest | `g4xu7-jiaaa-aaaan-aaaaq-cai` | +| IC mainnet | testnet | `g4xu7-jiaaa-aaaan-aaaaq-cai` | +| IC mainnet | mainnet | `ghsi2-tqaaa-aaaan-aaaca-cai` | + +The `BITCOIN_NETWORK` environment variable controls which network and canister to use. It is configured per environment in `icp.yaml`. + +## Prerequisites + +- [icp-cli](https://github.com/dfinity/icp-cli) installed +- [Docker](https://docs.docker.com/get-docker/) installed and running (optional, but recommended) + +> **Note:** Docker is used in this guide to run `bitcoind` for simplicity. If you prefer, you can install and run `bitcoind` natively instead — just make sure it is listening on the same ports (`18443` for RPC, `18444` for P2P) with the same credentials. + +## Getting Started + +Start a Bitcoin regtest node: + +```bash +docker run -d --name bitcoind \ + -p 18443:18443 -p 18444:18444 \ + lncm/bitcoind:v27.2 \ + -regtest -server -rpcbind=0.0.0.0 -rpcallowip=0.0.0.0/0 \ + -rpcuser=ic-btc-integration -rpcpassword=ic-btc-integration \ + -fallbackfee=0.00001 -txindex=1 +``` + +Start the local IC network and deploy: + +```bash +icp network start -d +icp deploy +``` + +## Usage + +Verify the configured network and Bitcoin canister ID: + +```bash +icp canister call backend get_config '()' +``` + +Create a wallet and get a Bitcoin address: + +```bash +docker exec bitcoind bitcoin-cli -regtest \ + -rpcuser=ic-btc-integration -rpcpassword=ic-btc-integration \ + createwallet "default" + +ADDR=$(docker exec bitcoind bitcoin-cli -regtest \ + -rpcuser=ic-btc-integration -rpcpassword=ic-btc-integration \ + getnewaddress) +``` + +Check the balance (should be 0): + +```bash +icp canister call backend get_balance "(\"$ADDR\")" +``` + +Mine a block to the address (each block rewards 50 BTC): + +```bash +docker exec bitcoind bitcoin-cli -regtest \ + -rpcuser=ic-btc-integration -rpcpassword=ic-btc-integration \ + generatetoaddress 1 "$ADDR" +``` + +Check the balance again (should be 5,000,000,000 satoshis = 50 BTC): + +> **Note:** Coinbase rewards require 100 confirmations before they can be spent. If you extend this example to send transactions, mine at least 101 blocks so the first block's reward becomes spendable. + +```bash +icp canister call backend get_balance "(\"$ADDR\")" +``` + +## Cleanup + +```bash +icp network stop +docker stop bitcoind && docker rm bitcoind +``` + +## Environments + +| Environment | IC Network | Bitcoin Network | Usage | +|-------------|-----------|----------------|-------| +| `local` | Local (PocketIC) | regtest | `icp deploy` | +| `staging` | IC mainnet | testnet | `icp deploy --env staging` | +| `production` | IC mainnet | mainnet | `icp deploy --env production` | + +## Cycle Costs + +Bitcoin canister API calls require cycles. The canister must attach cycles when calling the Bitcoin canister — the Rust CDK handles this automatically, while the Motoko backend attaches them explicitly via `(with cycles = amount)`. + +| API Call | Testnet / Regtest | Mainnet | +|----------|-------------------|---------| +| `bitcoin_get_balance` | 40,000,000 | 100,000,000 | +| `bitcoin_get_utxos` | 4,000,000,000 | 10,000,000,000 | +| `bitcoin_send_transaction` | 2,000,000,000 | 5,000,000,000 | + +See [Bitcoin API costs](https://docs.internetcomputer.org/references/bitcoin-how-it-works#api-fees-and-pricing) for the full reference. + +## Learn More + +- [Bitcoin Canister API Specification](https://github.com/dfinity/bitcoin-canister/blob/master/INTERFACE_SPECIFICATION.md) — full API reference (get_utxos, send_transaction, fee percentiles, etc.) +- [Developer Docs](https://docs.internetcomputer.org/build-on-btc) - How to build on Bitcoin diff --git a/bitcoin-starter/cargo-generate.toml b/bitcoin-starter/cargo-generate.toml new file mode 100644 index 0000000..6a21161 --- /dev/null +++ b/bitcoin-starter/cargo-generate.toml @@ -0,0 +1,15 @@ +[template] +description = "ICP project with local Bitcoin integration" + +[placeholders] +backend_type = { type = "string", prompt = "Choose your backend language:", choices = ["rust", "motoko"], default = "rust" } +network_type = { type = "string", prompt = "Use the default network or a dockerized one?", choices = ["Default", "Docker"], default = "Default" } + +[conditional.'backend_type == "rust"'] +ignore = [ "motoko-backend" ] + +[conditional.'backend_type == "motoko"'] +ignore = [ "rust-backend" ] + +[hooks] +pre = ["rename-backend-dir.rhai"] diff --git a/bitcoin-starter/icp.yaml b/bitcoin-starter/icp.yaml new file mode 100644 index 0000000..434021a --- /dev/null +++ b/bitcoin-starter/icp.yaml @@ -0,0 +1,42 @@ +# yaml-language-server: $schema=https://github.com/dfinity/icp-cli/raw/refs/tags/v0.2.0/docs/schemas/icp-yaml-schema.json + +canisters: + - backend + +{% if network_type == "Docker" -%} +networks: + - name: local + mode: managed + image: ghcr.io/dfinity/icp-cli-network-launcher + port-mapping: + - 0:4943 + args: + - "--bitcoind-addr=host.docker.internal:18444" +{% else -%} +networks: + - name: local + mode: managed + bitcoind-addr: + - "127.0.0.1:18444" +{% endif %} +environments: + - name: local + network: local + settings: + backend: + environment_variables: + BITCOIN_NETWORK: "regtest" + + - name: staging + network: ic + settings: + backend: + environment_variables: + BITCOIN_NETWORK: "testnet" + + - name: production + network: ic + settings: + backend: + environment_variables: + BITCOIN_NETWORK: "mainnet" diff --git a/bitcoin-starter/motoko-backend/.gitignore b/bitcoin-starter/motoko-backend/.gitignore new file mode 100644 index 0000000..4b5e51a --- /dev/null +++ b/bitcoin-starter/motoko-backend/.gitignore @@ -0,0 +1,2 @@ +.vessel +.mops diff --git a/bitcoin-starter/motoko-backend/backend.did b/bitcoin-starter/motoko-backend/backend.did new file mode 100644 index 0000000..6c70901 --- /dev/null +++ b/bitcoin-starter/motoko-backend/backend.did @@ -0,0 +1,13 @@ +type satoshi = nat64; + +type bitcoin_address = text; + +type bitcoin_config = record { + network : text; + bitcoin_canister_id : text; +}; + +service : { + "get_balance" : (bitcoin_address) -> (satoshi); + "get_config" : () -> (bitcoin_config) query; +} diff --git a/bitcoin-starter/motoko-backend/canister.yaml b/bitcoin-starter/motoko-backend/canister.yaml new file mode 100644 index 0000000..3876983 --- /dev/null +++ b/bitcoin-starter/motoko-backend/canister.yaml @@ -0,0 +1,8 @@ +# yaml-language-server: $schema=https://github.com/dfinity/icp-cli/raw/refs/tags/v0.2.0/docs/schemas/canister-yaml-schema.json + +name: backend +recipe: + type: "@dfinity/motoko@v4.1.0" + configuration: + main: src/main.mo + candid: backend.did diff --git a/bitcoin-starter/motoko-backend/mops.toml b/bitcoin-starter/motoko-backend/mops.toml new file mode 100644 index 0000000..7a68148 --- /dev/null +++ b/bitcoin-starter/motoko-backend/mops.toml @@ -0,0 +1,5 @@ +[dependencies] +core = "2.1.0" + +[toolchain] +moc = "1.3.0" diff --git a/bitcoin-starter/motoko-backend/src/main.mo b/bitcoin-starter/motoko-backend/src/main.mo new file mode 100644 index 0000000..9dd528d --- /dev/null +++ b/bitcoin-starter/motoko-backend/src/main.mo @@ -0,0 +1,88 @@ +/// Minimal Bitcoin integration example for the Internet Computer. +/// +/// Demonstrates reading Bitcoin balance via the Bitcoin canister API. + +import Runtime "mo:core/Runtime"; +import Text "mo:core/Text"; + +persistent actor Backend { + public type Satoshi = Nat64; + public type BitcoinAddress = Text; + + public type Network = { + #mainnet; + #testnet; + #regtest; + }; + + public type BitcoinConfig = { + network : Text; + bitcoin_canister_id : Text; + }; + + type BitcoinCanister = actor { + bitcoin_get_balance : shared { + address : BitcoinAddress; + network : Network; + min_confirmations : ?Nat32; + } -> async Satoshi; + }; + + // The BITCOIN_NETWORK env var (and thus the targeted Bitcoin network) + // can be changed at runtime without redeploying the canister. + private func getNetwork() : Network { + switch (Runtime.envVar("BITCOIN_NETWORK")) { + case (?value) { + switch (Text.toLower(value)) { + case ("mainnet") #mainnet; + case ("testnet") #testnet; + case _ #regtest; + }; + }; + case null #regtest; + }; + }; + + private func getBitcoinCanisterId(network : Network) : Text { + switch (network) { + case (#mainnet) "ghsi2-tqaaa-aaaan-aaaca-cai"; + case _ "g4xu7-jiaaa-aaaan-aaaaq-cai"; + }; + }; + + // Minimum cycles required for bitcoin_get_balance + // (100M for mainnet, 40M for testnet/regtest). + // See https://docs.internetcomputer.org/references/bitcoin-how-it-works#api-fees-and-pricing + private func getBalanceCost(network : Network) : Nat { + switch (network) { + case (#mainnet) 100_000_000; + case _ 40_000_000; + }; + }; + + /// Get the balance of a Bitcoin address in satoshis. + public func get_balance(address : BitcoinAddress) : async Satoshi { + let network = getNetwork(); + await (with cycles = getBalanceCost(network)) (actor (getBitcoinCanisterId(network)) : BitcoinCanister).bitcoin_get_balance({ + address; + network; + min_confirmations = null; + }); + }; + + /// Get the canister's Bitcoin configuration. + // Note: Runtime.envVar requires `system` capability which is not + // available in Motoko query functions, so we use a cached value here. + transient let cachedNetwork : Network = getNetwork(); + + public query func get_config() : async BitcoinConfig { + { + network = switch (cachedNetwork) { + case (#mainnet) "mainnet"; + case (#testnet) "testnet"; + case (#regtest) "regtest"; + }; + bitcoin_canister_id = getBitcoinCanisterId(cachedNetwork); + }; + }; +}; diff --git a/bitcoin-starter/rename-backend-dir.rhai b/bitcoin-starter/rename-backend-dir.rhai new file mode 100644 index 0000000..18e1a8e --- /dev/null +++ b/bitcoin-starter/rename-backend-dir.rhai @@ -0,0 +1,15 @@ +// This script renames the `-backend` directory to `backend` +// By the time this script is called the conditionals would have filtered +// all the unselected backend folders + +let backend_type = variable::get("backend_type"); +debug(`backend_type: ${backend_type}`); + +switch backend_type { + "rust" => { + file::rename("rust-backend", "backend"); + } + "motoko" => { + file::rename("motoko-backend", "backend"); + } +} diff --git a/bitcoin-starter/rust-backend/.gitignore b/bitcoin-starter/rust-backend/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/bitcoin-starter/rust-backend/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/bitcoin-starter/rust-backend/Cargo.toml b/bitcoin-starter/rust-backend/Cargo.toml new file mode 100644 index 0000000..d71393e --- /dev/null +++ b/bitcoin-starter/rust-backend/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "backend" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +candid = "0.10" +ic-cdk = "0.19" diff --git a/bitcoin-starter/rust-backend/backend.did b/bitcoin-starter/rust-backend/backend.did new file mode 100644 index 0000000..6c70901 --- /dev/null +++ b/bitcoin-starter/rust-backend/backend.did @@ -0,0 +1,13 @@ +type satoshi = nat64; + +type bitcoin_address = text; + +type bitcoin_config = record { + network : text; + bitcoin_canister_id : text; +}; + +service : { + "get_balance" : (bitcoin_address) -> (satoshi); + "get_config" : () -> (bitcoin_config) query; +} diff --git a/bitcoin-starter/rust-backend/canister.yaml b/bitcoin-starter/rust-backend/canister.yaml new file mode 100644 index 0000000..0c7891a --- /dev/null +++ b/bitcoin-starter/rust-backend/canister.yaml @@ -0,0 +1,12 @@ +# yaml-language-server: $schema=https://github.com/dfinity/icp-cli/raw/refs/tags/v0.2.0/docs/schemas/canister-yaml-schema.json + +name: backend +recipe: + type: "@dfinity/rust@v3.2.0" + configuration: + package: backend + shrink: true + candid: backend.did + metadata: + - name: "crate:version" + value: 1.0.0 diff --git a/bitcoin-starter/rust-backend/src/lib.rs b/bitcoin-starter/rust-backend/src/lib.rs new file mode 100644 index 0000000..2b9c83f --- /dev/null +++ b/bitcoin-starter/rust-backend/src/lib.rs @@ -0,0 +1,57 @@ +//! Minimal Bitcoin integration example for the Internet Computer. +//! +//! Demonstrates reading Bitcoin balance via the Bitcoin canister API. + +use candid::CandidType; +use ic_cdk::bitcoin_canister::{ + bitcoin_get_balance, get_bitcoin_canister_id, GetBalanceRequest, Network, Satoshi, +}; + +// The BITCOIN_NETWORK env var (and thus the targeted Bitcoin network) +// can be changed at runtime without redeploying the canister. +fn get_network() -> Network { + let network_str = if ic_cdk::api::env_var_name_exists("BITCOIN_NETWORK") { + ic_cdk::api::env_var_value("BITCOIN_NETWORK").to_lowercase() + } else { + "regtest".to_string() + }; + + match network_str.as_str() { + "mainnet" => Network::Mainnet, + "testnet" => Network::Testnet, + _ => Network::Regtest, + } +} + +/// Get the balance of a Bitcoin address in satoshis. +#[ic_cdk::update] +async fn get_balance(address: String) -> Satoshi { + bitcoin_get_balance(&GetBalanceRequest { + address, + network: get_network(), + min_confirmations: None, + }) + .await + .expect("Failed to get balance") +} + +/// Get the canister's Bitcoin configuration. +#[ic_cdk::query] +fn get_config() -> BitcoinConfig { + let network = get_network(); + BitcoinConfig { + network: match network { + Network::Mainnet => "mainnet", + Network::Testnet => "testnet", + Network::Regtest => "regtest", + } + .to_string(), + bitcoin_canister_id: get_bitcoin_canister_id(&network).to_string(), + } +} + +#[derive(CandidType)] +struct BitcoinConfig { + network: String, + bitcoin_canister_id: String, +} diff --git a/cargo-generate.toml b/cargo-generate.toml index f76c2d2..b747dd0 100644 --- a/cargo-generate.toml +++ b/cargo-generate.toml @@ -1,6 +1,7 @@ [template] sub_templates = [ "hello-world", + "bitcoin-starter", "motoko", "proxy", "rust", diff --git a/rust/Cargo.lock b/rust/Cargo.lock new file mode 100644 index 0000000..631fbba --- /dev/null +++ b/rust/Cargo.lock @@ -0,0 +1,733 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anyhow" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" + +[[package]] +name = "ar_archive_writer" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b" +dependencies = [ + "object", +] + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "backend" +version = "0.1.0" +dependencies = [ + "candid", + "ic-cdk", +] + +[[package]] +name = "binread" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16598dfc8e6578e9b597d9910ba2e73618385dc9f4b1d43dd92c349d6be6418f" +dependencies = [ + "binread_derive", + "lazy_static", + "rustversion", +] + +[[package]] +name = "binread_derive" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d9672209df1714ee804b1f4d4f68c8eb2a90b1f7a07acf472f88ce198ef1fed" +dependencies = [ + "either", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "candid" +version = "0.10.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e49a6e53730e2d41f6fc3ad9ef4d9bc7ad738ddc6aed4ceb30a35a2cd63e9bcb" +dependencies = [ + "anyhow", + "binread", + "byteorder", + "candid_derive", + "hex", + "ic_principal", + "leb128", + "num-bigint", + "num-traits", + "paste", + "pretty", + "serde", + "serde_bytes", + "stacker", + "thiserror 1.0.69", +] + +[[package]] +name = "candid_derive" +version = "0.10.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab75e3283c7912bb2986dd7033a87e4e5f3f472158816308a04d40f5d697099c" +dependencies = [ + "lazy_static", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "cc" +version = "1.2.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b26a0954ae34af09b50f0de26458fa95369a0d478d8236d3f93082b219bd29" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.114", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "data-encoding" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "ic-cdk" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "818d6d5416a8f0212e1b132703b0da51e36c55f2b96677e96f2bbe7702e1bd85" +dependencies = [ + "candid", + "ic-cdk-executor", + "ic-cdk-macros", + "ic-error-types", + "ic-management-canister-types", + "ic0", + "pin-project-lite", + "serde", + "serde_bytes", + "slotmap", + "thiserror 2.0.18", +] + +[[package]] +name = "ic-cdk-executor" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33716b730ded33690b8a704bff3533fda87d229e58046823647d28816e9bcee7" +dependencies = [ + "ic0", + "slotmap", + "smallvec", +] + +[[package]] +name = "ic-cdk-macros" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66dad91a214945cb3605bc9ef6901b87e2ac41e3624284c2cabba49d43aa4f43" +dependencies = [ + "candid", + "darling", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "ic-error-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbeeb3d91aa179d6496d7293becdacedfc413c825cac79fd54ea1906f003ee55" +dependencies = [ + "serde", + "strum", + "strum_macros", +] + +[[package]] +name = "ic-management-canister-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3149217e24186df3f13dc45eee14cdb3e5cad07d0b2b67bd53555c1c55462957" +dependencies = [ + "candid", + "serde", + "serde_bytes", +] + +[[package]] +name = "ic0" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1499d08fd5be8f790d477e1865d63bab6a8d748300e141270c4296e6d5fdd6bc" + +[[package]] +name = "ic_principal" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2b6c5941dfd659e77b262342fa58ad49489367ad026255cda8c43682d0c534" +dependencies = [ + "crc32fast", + "data-encoding", + "serde", + "sha2", + "thiserror 1.0.69", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + +[[package]] +name = "libc" +version = "0.2.180" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pretty" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d22152487193190344590e4f30e219cf3fe140d9e7a3fdb683d82aa2c5f4156" +dependencies = [ + "arrayvec", + "typed-arena", + "unicode-width", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "psm" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa96cb91275ed31d6da3e983447320c4eb219ac180fa1679a0889ff32861e2d" +dependencies = [ + "ar_archive_writer", + "cc", +] + +[[package]] +name = "quote" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_bytes" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slotmap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "stacker" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1f8b29fb42aafcea4edeeb6b2f2d7ecd0d969c48b4cf0d2e64aafc471dd6e59" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.114", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "typed-arena" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"