Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(backend): Expose and use min_confirmations filter to get utxos #2601

Merged
merged 2 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/backend/backend.did
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ type SelectedUtxosFeeRequest = record {
network : BitcoinNetwork;
amount_satoshis : nat64;
source_address : text;
min_confirmations : opt nat32;
};
type SelectedUtxosFeeResponse = record {
fee_satoshis : nat64;
Expand Down
16 changes: 11 additions & 5 deletions src/backend/src/bitcoin_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ use ic_cdk::api::management_canister::bitcoin::{
async fn get_utxos(
network: BitcoinNetwork,
address: String,
maybe_next_page: Option<Vec<u8>>,
filter: Option<UtxoFilter>,
) -> Result<GetUtxosResponse, String> {
let utxos_res = bitcoin_get_utxos(GetUtxosRequest {
address,
network,
filter: maybe_next_page.map(UtxoFilter::Page),
filter,
})
.await
.map_err(|err| err.1)?;
Expand All @@ -25,13 +25,19 @@ async fn get_utxos(
}
/// Returns all the UTXOs of a specific address.
/// API interface returns a paginated view of the utxos but we need to get them all.
pub async fn get_all_utxos(network: BitcoinNetwork, address: String) -> Result<Vec<Utxo>, String> {
let mut utxos_response = get_utxos(network, address.clone(), None).await?;
pub async fn get_all_utxos(
network: BitcoinNetwork,
address: String,
min_confirmations: Option<u32>,
) -> Result<Vec<Utxo>, String> {
let filter = min_confirmations.map(UtxoFilter::MinConfirmations);
let mut utxos_response = get_utxos(network, address.clone(), filter).await?;

let mut all_utxos: Vec<Utxo> = utxos_response.utxos;
let mut next_page: Option<Vec<u8>> = utxos_response.next_page;
while next_page.is_some() {
utxos_response = get_utxos(network, address.clone(), next_page).await?;
utxos_response =
get_utxos(network, address.clone(), next_page.map(UtxoFilter::Page)).await?;
all_utxos.extend(utxos_response.utxos);
next_page = utxos_response.next_page;
}
Expand Down
16 changes: 13 additions & 3 deletions src/backend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,13 +285,23 @@ fn list_custom_tokens() -> Vec<CustomToken> {
read_state(|s| s.custom_token.get(&stored_principal).unwrap_or_default().0)
}

const MIN_CONFIRMATIONS_ACCEPTED_BTC_TX: u32 = 6;

#[update(guard = "may_read_user_data")]
async fn btc_select_user_utxos_fee(
params: SelectedUtxosFeeRequest,
) -> Result<SelectedUtxosFeeResponse, SelectedUtxosFeeError> {
let all_utxos = bitcoin_api::get_all_utxos(params.network, params.source_address)
.await
.map_err(|msg| SelectedUtxosFeeError::InternalError { msg })?;
let all_utxos = bitcoin_api::get_all_utxos(
params.network,
params.source_address,
Some(
params
.min_confirmations
.unwrap_or(MIN_CONFIRMATIONS_ACCEPTED_BTC_TX),
),
)
.await
.map_err(|msg| SelectedUtxosFeeError::InternalError { msg })?;

let median_fee_millisatoshi_per_vbyte = bitcoin_api::get_fee_per_byte(params.network)
.await
Expand Down
2 changes: 2 additions & 0 deletions src/backend/tests/it/bitcoin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ fn test_select_user_utxos_fee_returns_zero_when_user_has_insufficient_funds() {
amount_satoshis: 100_000_000u64,
source_address: "bcrt1qpg7udjvq7gx2fp480pgt4hnhj3qc4nhrkstc33".to_string(),
network: BitcoinNetwork::Regtest,
// Until bitcoin is supported in pocket-ic it only works with 1.
min_confirmations: Some(1),
};
let response = pic_setup.update::<Result<SelectedUtxosFeeResponse, SelectedUtxosFeeError>>(
caller,
Expand Down
1 change: 1 addition & 0 deletions src/declarations/backend/backend.did
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ type SelectedUtxosFeeRequest = record {
network : BitcoinNetwork;
amount_satoshis : nat64;
source_address : text;
min_confirmations : opt nat32;
};
type SelectedUtxosFeeResponse = record {
fee_satoshis : nat64;
Expand Down
1 change: 1 addition & 0 deletions src/declarations/backend/backend.did.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ export interface SelectedUtxosFeeRequest {
network: BitcoinNetwork;
amount_satoshis: bigint;
source_address: string;
min_confirmations: [] | [number];
}
export interface SelectedUtxosFeeResponse {
fee_satoshis: bigint;
Expand Down
3 changes: 2 additions & 1 deletion src/declarations/backend/backend.factory.certified.did.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ export const idlFactory = ({ IDL }) => {
const SelectedUtxosFeeRequest = IDL.Record({
network: BitcoinNetwork,
amount_satoshis: IDL.Nat64,
source_address: IDL.Text
source_address: IDL.Text,
min_confirmations: IDL.Opt(IDL.Nat32)
});
const Outpoint = IDL.Record({
txid: IDL.Vec(IDL.Nat8),
Expand Down
3 changes: 2 additions & 1 deletion src/declarations/backend/backend.factory.did.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ export const idlFactory = ({ IDL }) => {
const SelectedUtxosFeeRequest = IDL.Record({
network: BitcoinNetwork,
amount_satoshis: IDL.Nat64,
source_address: IDL.Text
source_address: IDL.Text,
min_confirmations: IDL.Opt(IDL.Nat32)
});
const Outpoint = IDL.Record({
txid: IDL.Vec(IDL.Nat8),
Expand Down
1 change: 1 addition & 0 deletions src/shared/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ pub mod bitcoin {
pub amount_satoshis: u64,
pub source_address: String,
pub network: BitcoinNetwork,
pub min_confirmations: Option<u32>,
}

#[derive(CandidType, Deserialize, Clone, Eq, PartialEq, Debug)]
Expand Down