Basic Bitcoin
This example demonstrates how to deploy a smart contract on the Internet Computer that can receive and send Bitcoin, including support for legacy (P2PKH), SegWit (P2WPKH), and Taproot (P2TR) address types.
Architecture
This example integrates with the Internet Computer's built-in:
For background on the ICP <-> BTC integration, refer to the Learn Hub.
Prerequisites
- Rust toolchain
- Internet Computer SDK
- Local installation of Bitcoin
- On macOS, an
llvm
version that supports thewasm32-unknown-unknown
target is required. This is because thebitcoin
library relies onsecp256k1-sys
, which requiresllvm
to build. The defaultllvm
version provided by XCode does not meet this requirement. Instead, install the Homebrew version, usingbrew install llvm
.
Step 1: Building and deploying the smart contract
Clone the examples repo
git clone https://github.com/dfinity/examples
cd examples/rust/basic_bitcoin
Start the ICP local development environment
dfx start --clean --background
Start the local Bitcoin testnet (regtest)
In a separate terminal window, run the following:
bitcoind -conf=$(pwd)/bitcoin.conf -datadir=$(pwd)/bitcoin_data --port=18444
Deploy the smart contract
dfx deploy basic_bitcoin --argument '(variant { regtest })'
What this does:
dfx deploy
tells the command line interface todeploy
the smart contract.--argument '(variant { regtest })'
passes the argumentregtest
to initialize the smart contract, telling it to connect to the local Bitcoin regtest network.
Your smart contract is live and ready to use! You can interact with it using either the command line or using the Candid UI, which is the link you see in the terminal.
[!NOTE] You can also interact with a pre-deployed version of the
basic_bitcoin
example running on the IC mainnet and configured to interact with Bitcoin testnet4.Access the Candid UI of the example: https://a4gq6-oaaaa-aaaab-qaa4q-cai.raw.icp0.io/?id=vvha6-7qaaa-aaaap-ahodq-cai
2. Supported Bitcoin address types
This example demonstrates how to generate and use the following address types:
- P2PKH (Legacy) using ECDSA and
sign_with_ecdsa
- P2WPKH (SegWit v0) using ECDSA and
sign_with_ecdsa
- P2TR (Taproot, key-path-only) using Schnorr keys and
sign_with_schnorr
- P2TR (Taproot, script-path-enabled) commits to a script allowing both key path and script path spending
Use the Candid UI or command line to generate these addresses with:
dfx canister call basic_bitcoin get_p2pkh_address
# or get_p2wpkh_address, get_p2tr_key_path_only_address, get_p2tr_script_path_enabled_address
3. Receiving bitcoin
Use the bitcoin-cli
to mine a Bitcoin block and send the block reward in the form of local testnet BTC to one of the smart contract addresses.
bitcoin-cli -conf=$(pwd)/bitcoin.conf generatetoaddress 1 <bitcoin_address>
4. Checking balance
Check the balance of any Bitcoin address:
dfx canister call basic_bitcoin get_balance '("<bitcoin_address>")'
This uses bitcoin_get_balance
and works for any supported address type. Requires at least one confirmation to be reflected.
5. Sending bitcoin
You can send BTC using the following endpoints:
send_from_p2pkh_address
send_from_p2wpkh_address
send_from_p2tr_key_path_only_address
send_from_p2tr_script_path_enabled_address_key_spend
send_from_p2tr_script_path_enabled_address_script_spend
Each endpoint internally:
- Estimates fees
- Looks up spendable UTXOs
- Builds a transaction to the target address
- Signs using ECDSA or Schnorr, depending on address type
- Broadcasts the transaction using
bitcoin_send_transaction
Example:
dfx canister call basic_bitcoin send_from_p2pkh_address '(record {
destination_address = "tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt";
amount_in_satoshi = 4321;
})'
[!IMPORTANT] Newly created bitcoin, like those you created with the above
bitcoin-cli
command cannot be spent until 10 additional blocks have been added to the chain. To make your bitcoin spendable, create 10 additional blocks. Choose one of the smart contract addresses as receiver of the block reward or use any valid bitcoin dummy address.bitcoin-cli -conf=$(pwd)/bitcoin.conf generatetoaddress 10 <bitcoin_address>
The function returns the transaction ID, which you can track on mempool.space testnet4.
6. Retrieving block headers
You can query historical block headers:
dfx canister call basic_bitcoin get_block_headers '(10: nat32)'
# or a range:
dfx canister call basic_bitcoin get_block_headers '(0: nat32, 11: nat32)'
This calls bitcoin_get_block_headers
, useful for validating blockchains or light client logic.
Notes on implementation
- Keys are derived using structured derivation paths according to BIP-32.
- Key caching is used to avoid repeated calls to
get_ecdsa_public_key
andget_schnorr_public_key
. - Transactions are assembled and signed manually, ensuring maximum flexibility in construction and fee estimation.
Conclusion
In this tutorial, you were able to:
- Deploy a smart contract locally that can receive & send bitcoin.
- Connect the smart contract to the local Bitcoin testnet.
- Send the smart contract some local testnet BTC.
- Check the local testnet BTC balance of the smart contract.
- Use the smart contract to send local testnet BTC to another local testnet BTC address.
The steps to develop Bitcoin dapps locally are extensively documented in this tutorial.
Note that for testing on mainnet, the chain-key testing canister can be used to save on costs for calling the threshold signing APIs for signing the BTC transactions.
Security considerations and best practices
If you base your application on this example, we recommend you familiarize yourself with and adhere to the security best practices for developing on the Internet Computer. This example may not implement all the best practices.
For example, the following aspects are particularly relevant for this app:
- Certify query responses if they are relevant for security, since the app e.g. offers a method to read balances.
- Use a decentralized governance system like SNS to make a smart contract have a decentralized controller, since decentralized control may be essential for smart contracts holding bitcoins on behalf of users.
Last updated: May 2025