Reading the Bitcoin state
Overview
To read information from the Bitcoin network's state, the Bitcoin integration API exposes the following methods:
bitcoin_get_utxos
: Returns the unspent transaction outputs (UTXOs) of a Bitcoin address.bitcoin_get_balance
: Returns the balance of a Bitcoin address.bitcoin_get_current_fee_percentiles
: Returns the percentiles of the fees for the last 10_000 transactions. Returns the fee in millisatoshi per virtual byte.bitcoin_get_block_headers
: Returns the block headers within a provided range of start height and end height (optional) for the specified Bitcoin network (testnet or mainnet).
Standard Bitcoin addresses (P2PKH, P2SH, P2WPKH, etc) are supported.
Bitcoin networks
Both the Bitcoin testnet and mainnet networks are supported. The network can be specified as a parameter in each function.
API costs
Each call to the API has a cost of cycles, therefore a sufficient amount of cycles must be sent with each call.
Cycles costs will vary between the Bitcoin testnet and mainnet. For a breakdown of the costs, see Bitcoin API costs.
Reading unspent transaction outputs
To read unspent transaction outputs from the Bitcoin network, make a call to the bitcoin_get_utxos
Bitcoin API method. You can create a function in your canister to call this method such as:
- Motoko
- Rust
type ManagementCanisterActor = actor {
bitcoin_get_utxos : GetUtxosRequest -> async GetUtxosResponse;
};
public func get_utxos(network : Network, address : BitcoinAddress) : async GetUtxosResponse {
ExperimentalCycles.add(GET_UTXOS_COST_CYCLES);
await management_canister_actor.bitcoin_get_utxos({
address;
network;
filter = null;
})
};
use ic_cdk::api::management_canister::bitcoin::{
BitcoinNetwork, GetBalanceRequest, GetCurrentFeePercentilesRequest, GetUtxosRequest,
GetUtxosResponse, MillisatoshiPerByte, Satoshi, SendTransactionRequest,
};
pub async fn get_utxos(network: BitcoinNetwork, address: String) -> GetUtxosResponse {
let utxos_res: Result<(GetUtxosResponse,), _> = call_with_payment(
Principal::management_canister(),
"bitcoin_get_utxos",
(GetUtxosRequest {
address,
network: network.into(),
filter: None,
},),
GET_UTXOS_COST_CYCLES,
)
.await;
utxos_res.unwrap().0
}
Then make a call to this canister's function with the command:
dfx canister --network ic call CANISTER_NAME get_utxos '("ADDRESS")'
Replace CANISTER_NAME
with your canister's name and replace ADDRESS
with your Bitcoin address. Learn more about Bitcoin addresses.
Reading current balance
To read the current balance of a Bitcoin address, make a call to the bitcoin_get_balance
Bitcoin API method. You can create a function in your canister to call this method such as:
- Motoko
- Rust
type ManagementCanisterActor = actor {
bitcoin_get_balance : GetBalanceRequest -> async Satoshi;
};
let management_canister_actor : ManagementCanisterActor = actor("aaaaa-aa");
public func get_balance(network : Network, address : BitcoinAddress) : async Satoshi {
ExperimentalCycles.add(GET_BALANCE_COST_CYCLES);
await management_canister_actor.bitcoin_get_balance({
address;
network;
min_confirmations = null;
})
};
use ic_cdk::api::management_canister::bitcoin::{
BitcoinNetwork, GetBalanceRequest, GetCurrentFeePercentilesRequest, GetUtxosRequest,
GetUtxosResponse, MillisatoshiPerByte, Satoshi, SendTransactionRequest,
};
pub async fn get_balance(network: BitcoinNetwork, address: String) -> u64 {
let balance_res: Result<(Satoshi,), _> = call_with_payment(
Principal::management_canister(),
"bitcoin_get_balance",
(GetBalanceRequest {
address,
network: network.into(),
min_confirmations: None,
},),
GET_BALANCE_COST_CYCLES,
)
.await;
balance_res.unwrap().0
}
Then make a call to this canister's function with the command:
dfx canister --network ic call CANISTER_NAME get_balance '("ADDRESS")'
Replace CANISTER_NAME
with your canister's name and replace ADDRESS
with your Bitcoin address. Learn more about Bitcoin addresses.
Reading the fee percentile
The transaction fees in the Bitcoin network change dynamically based on the number of pending transactions. In order to get fee percentiles for the last few blocks (4 - 10), call bitcoin_get_current_fee_percentiles
.
The function returns 101 numbers that are fees measured in millisatoshi per virtual byte. The i
-th element of the result corresponds to the i
-th percentile fee. For example, to get the median fee over the last few blocks, look at the 50
-th element of the result.
- Motoko
- Rust
type ManagementCanisterActor = actor {
bitcoin_get_current_fee_percentiles : GetCurrentFeePercentilesRequest -> async [MillisatoshiPerVByte];
};
let management_canister_actor : ManagementCanisterActor = actor("aaaaa-aa");
public func get_current_fee_percentiles(network : Network) : async [MillisatoshiPerVByte] {
ExperimentalCycles.add(GET_CURRENT_FEE_PERCENTILES_COST_CYCLES);
await management_canister_actor.bitcoin_get_current_fee_percentiles({
network;
})
};
use ic_cdk::api::management_canister::bitcoin::{
BitcoinNetwork, GetBalanceRequest, GetCurrentFeePercentilesRequest, GetUtxosRequest,
GetUtxosResponse, MillisatoshiPerByte, Satoshi, SendTransactionRequest,
};
pub async fn get_current_fee_percentiles(network: BitcoinNetwork) -> Vec<MillisatoshiPerByte> {
let res: Result<(Vec<MillisatoshiPerByte>,), _> = call_with_payment(
Principal::management_canister(),
"bitcoin_get_current_fee_percentiles",
(GetCurrentFeePercentilesRequest {
network: network.into(),
},),
GET_CURRENT_FEE_PERCENTILES_CYCLES,
)
.await;
res.unwrap().0
}
Then make a call to this canister's function with the command:
dfx canister --network ic call CANISTER_NAME get_current_fee_percentiles
Replace CANISTER_NAME
with your canister's name.