Skip to main content
ckBTC (chain-key Bitcoin) is an ICRC-1 token on the Internet Computer that is backed 1:1 by Bitcoin held by a canister smart contract. The ckBTC minter canister manages the conversion between BTC and ckBTC, enabling users to interact with Bitcoin through the speed and low cost of the Internet Computer.

Overview

ckBTC combines the value of Bitcoin with the programmability and efficiency of the Internet Computer:
  • 1:1 Backed: Every ckBTC token is backed by real BTC held in the minter’s custody
  • Fast Transactions: ckBTC transfers finalize in 1-2 seconds
  • Low Fees: Transaction fees are ~0.0000001 BTC compared to on-chain Bitcoin fees
  • Programmable: Can be used in canister smart contracts and DeFi applications

Architecture

Minter Canister

The ckBTC minter (rs/bitcoin/ckbtc/minter) is the core component managing conversions between BTC and ckBTC.

Key Responsibilities

BTC → ckBTC

Detects Bitcoin deposits and mints ckBTC tokens

ckBTC → BTC

Burns ckBTC and sends Bitcoin to destination addresses

UTXO Management

Tracks and manages Bitcoin UTXOs owned by the minter

Fee Estimation

Estimates Bitcoin transaction fees dynamically

Minter Configuration

// From rs/bitcoin/ckbtc/minter/ckbtc_minter.did
type InitArgs = record {
    btc_network : BtcNetwork;
    ledger_id : principal;
    ecdsa_key_name : text;
    deposit_btc_min_amount : opt nat64;
    retrieve_btc_min_amount : nat64;
    max_time_in_queue_nanos : nat64;
    min_confirmations : opt nat32;
    mode : Mode;
    check_fee : opt nat64;
    btc_checker_principal: opt principal;
};
  • btc_network: Bitcoin network (Mainnet/Testnet/Regtest)
  • ledger_id: Principal of the ckBTC ledger canister
  • ecdsa_key_name: Name of the threshold ECDSA key to use
  • deposit_btc_min_amount: Minimum BTC amount for deposits (prevents dust)
  • retrieve_btc_min_amount: Minimum ckBTC amount for withdrawals
  • max_time_in_queue_nanos: Maximum time a withdrawal can be queued
  • min_confirmations: Required confirmations for Bitcoin transactions (default: 12)
  • mode: Operation mode (ReadOnly/RestrictedTo/GeneralAvailability)
  • check_fee: Fee charged for Bitcoin address validation
  • btc_checker_principal: Optional KYC/AML checker canister

BTC to ckBTC (Deposit)

Users deposit Bitcoin to a unique address controlled by the minter to receive ckBTC.

Deposit Process

1

Get Bitcoin Address

Call get_btc_address to obtain a unique Bitcoin address:
get_btc_address : (record { 
    owner: opt principal; 
    subaccount : opt blob 
}) -> (text);
Each IC account (principal + subaccount) maps to a unique Bitcoin address.
2

Send Bitcoin

Transfer BTC from your Bitcoin wallet to the address from step 1.
Send at least the minimum deposit amount (check deposit_btc_min_amount in get_minter_info).
3

Wait for Confirmations

Wait for the required number of confirmations (typically 12 blocks, ~2 hours).
4

Update Balance

Call update_balance to trigger minting:
update_balance : (record { 
    owner: opt principal; 
    subaccount : opt blob 
}) -> (variant { 
    Ok : vec UtxoStatus; 
    Err : UpdateBalanceError 
});
The minter will mint ckBTC (minus fees) to your IC account.

UTXO Processing

The minter processes UTXOs in several states:
// From rs/bitcoin/ckbtc/minter/ckbtc_minter.did
type UtxoStatus = variant {
    ValueTooSmall : Utxo;
    Tainted : Utxo;
    Checked : Utxo;
    Minted : record {
        block_index : nat64;
        minted_amount : nat64;
        utxo : Utxo;
    };
};
  • ValueTooSmall: UTXO value is below the minimum deposit amount
  • Tainted: UTXO flagged by the Bitcoin checker (KYC/AML)
  • Checked: UTXO passed checks but minting not yet complete
  • Minted: ckBTC successfully minted, includes ledger block index

ckBTC to BTC (Withdrawal)

Users burn ckBTC to receive Bitcoin at a specified address.

Withdrawal Process

1

Approve Minter

First-time withdrawals require approving the minter on the ledger:
icrc2_approve : (record {
    spender : record { owner : principal };
    amount : nat;
}) -> (variant { Ok : nat; Err : ApproveError });
2

Request Withdrawal

Call retrieve_btc_with_approval to initiate withdrawal:
retrieve_btc_with_approval : (record {
    address : text;
    amount : nat64;
    from_subaccount : opt blob;
}) -> (variant { 
    Ok : RetrieveBtcOk; 
    Err : RetrieveBtcWithApprovalError 
});
Returns a block_index for tracking the withdrawal.
3

Track Status

Monitor withdrawal progress:
retrieve_btc_status_v2 : (record { 
    block_index : nat64 
}) -> (RetrieveBtcStatusV2) query;

Withdrawal States

type RetrieveBtcStatusV2 = variant {
    Unknown;
    Pending;
    Signing;
    Sending : record { txid : blob };
    Submitted : record { txid : blob };
    AmountTooLow;
    Confirmed : record { txid : blob };
    Reimbursed : ReimbursedDeposit;
    WillReimburse : ReimbursementRequest;
};
  1. Pending: Request queued, waiting to be batched
  2. Signing: Obtaining threshold ECDSA signature
  3. Sending: Signed transaction being broadcast
  4. Submitted: Transaction submitted to Bitcoin network
  5. Confirmed: Transaction confirmed on Bitcoin blockchain
  6. Reimbursed: Failed transaction, ckBTC refunded

Transaction Batching

// From rs/bitcoin/ckbtc/minter/src/lib.rs
pub const MIN_PENDING_REQUESTS: usize = 20;
pub const MAX_REQUESTS_PER_BATCH: usize = 100;
The minter batches withdrawal requests to optimize Bitcoin transaction fees:
  • Waits for at least 20 pending requests before creating a batch
  • Includes up to 100 requests in a single Bitcoin transaction
  • Distributes transaction fees across all recipients
  • Creates change outputs back to the minter

UTXO Management

The minter maintains a pool of UTXOs for efficient transaction construction.

UTXO Selection Algorithm

// From rs/bitcoin/ckbtc/minter/src/lib.rs
fn utxos_selection(
    target: u64, 
    available_utxos: &mut UtxoSet, 
    output_count: usize
) -> Vec<Utxo>
The selection algorithm:
  1. Greedily selects smallest UTXOs that sum to at least the target amount
  2. If managing >1,000 UTXOs, matches input count to output count + 1
  3. Returns empty vector if insufficient funds

UTXO Consolidation

pub const MIN_CONSOLIDATION_INTERVAL: Duration = Duration::from_secs(24 * 60 * 60);
pub const UTXOS_COUNT_THRESHOLD: usize = 1_000;
The minter automatically consolidates UTXOs when:
  • UTXO count exceeds the threshold
  • At least 24 hours have passed since last consolidation
  • Bitcoin network fees are reasonable
Consolidation reduces future transaction fees by combining many small UTXOs into fewer large ones.

Fee Structure

Deposit Fees

When converting BTC → ckBTC:
pub const CKBTC_LEDGER_MEMO_SIZE: u16 = 80;
  • Bitcoin Transaction Fee: Paid by the user when sending BTC
  • Check Fee: Optional fee for KYC/AML validation (if enabled)
  • Ledger Fee: 10 satoshis for minting ckBTC

Withdrawal Fees

When converting ckBTC → BTC:
type WithdrawalFee = record {
    minter_fee : nat64;     // Fee charged by minter
    bitcoin_fee : nat64;    // Estimated Bitcoin network fee
};
Estimate withdrawal fees:
estimate_withdrawal_fee : (record { 
    amount : opt nat64 
}) -> (record { 
    bitcoin_fee : nat64; 
    minter_fee : nat64 
}) query;
The minter dynamically estimates fees based on:
  • Current Bitcoin network fee rate (from Bitcoin canister)
  • Transaction size (inputs + outputs)
  • Required confirmations

Fee Distribution

// From rs/bitcoin/ckbtc/minter/src/lib.rs
fn distribute(amount: u64, n: u64) -> Vec<u64>
For batched transactions, fees are distributed fairly:
  • Each recipient pays a proportional share
  • Remainder distributed to early recipients
  • Example: distribute(5 satoshis, 3 recipients) = [2, 2, 1]

Transaction Building

The minter constructs Bitcoin transactions with multiple outputs.

Unsigned Transaction

pub struct UnsignedTransaction {
    inputs: Vec<UnsignedInput>,
    outputs: Vec<TxOut>,
    lock_time: u32,
}

Transaction Features

Replace-By-Fee (RBF) allows resubmitting with higher fees:
const SEQUENCE_RBF_ENABLED: u32 = 0xfffffffd;
Used when transactions get stuck in the mempool.

Transaction Resubmission

pub const MIN_RESUBMISSION_DELAY: Duration = Duration::from_secs(24 * 60 * 60);
If a transaction doesn’t confirm within expected time:
  1. Wait at least 24 hours
  2. Check if transaction is in mempool (0 confirmations)
  3. Create replacement transaction with higher fee
  4. Increase fee by at least 10% from previous attempt
  5. Resubmit to Bitcoin network

Security Features

The ckBTC minter implements multiple security measures to protect user funds:

Address Validation

pub enum BtcAddressCheckStatus {
    Valid,
    Invalid(String),
}
Withdrawal addresses are validated for:
  • Correct format (P2PKH, P2SH, P2WPKH, P2WSH, P2TR)
  • Network compatibility (mainnet vs testnet)
  • Optional KYC/AML screening via Bitcoin checker

Transaction Limits

const MAX_INPUTS_PER_TX: usize = 500;
  • Maximum inputs: Prevents creating non-standard transactions >100KB
  • Minimum withdrawal: Ensures amount covers Bitcoin transaction fees
  • Dust limit: Prevents outputs too small for the Bitcoin network

Reimbursement System

Failed transactions are automatically reimbursed:
type ReimbursementReason = variant {
    CallFailed;
    TaintedDestination : record {
        kyt_fee : nat64;
        kyt_provider: principal;
    };
};
Reimbursements occur when:
  • Bitcoin checker rejects the destination address
  • Transaction building fails (e.g., too many inputs)
  • Ledger calls fail during processing

Event Logging

The minter maintains a comprehensive event log for auditability.

Event Types

type EventType = variant {
    init : InitArgs;
    upgrade : UpgradeArgs;
    received_utxos : record { /* ... */ };
    accepted_retrieve_btc_request : record { /* ... */ };
    sent_transaction : record { /* ... */ };
    confirmed_transaction : record { txid : blob };
    checked_utxo : record { /* ... */ };
    schedule_deposit_reimbursement : record { /* ... */ };
    reimbursed_failed_deposit : record { /* ... */ };
    // ... more event types
};

Querying Events

get_events : (record { 
    start: nat64; 
    length : nat64 
}) -> (vec Event) query;
Events provide full transparency into minter operations.

Candid Interface

Core Methods

// Get deposit address
get_btc_address : (
    record { 
        owner: opt principal; 
        subaccount : opt blob 
    }
) -> (text);

// Mint ckBTC from Bitcoin deposit
update_balance : (
    record { 
        owner: opt principal; 
        subaccount : opt blob 
    }
) -> (variant { 
    Ok : vec UtxoStatus; 
    Err : UpdateBalanceError 
});

Bitcoin Integration

Underlying Bitcoin protocol integration

ICRC-1 Ledger

ckBTC implements the ICRC-1 token standard

Source Code Reference

Key files in the ckBTC implementation:
  • rs/bitcoin/ckbtc/minter/src/lib.rs - Main minter logic
  • rs/bitcoin/ckbtc/minter/src/state/ - State management
  • rs/bitcoin/ckbtc/minter/src/updates/ - Update call handlers
  • rs/bitcoin/ckbtc/minter/src/queries/ - Query call handlers
  • rs/bitcoin/ckbtc/minter/src/tx.rs - Transaction building
  • rs/bitcoin/ckbtc/minter/ckbtc_minter.did - Candid interface

Build docs developers (and LLMs) love