From 6acbb54bd33b89baf1dd5b7ecbd0dc9ce9eed361 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lloren=C3=A7?= Date: Tue, 1 Oct 2024 11:49:26 +0200 Subject: [PATCH 1/2] feat(backend): Expose and use min_confirmations filter to get utxos --- src/backend/src/bitcoin_api.rs | 16 +++++++++++----- src/backend/src/lib.rs | 12 +++++++++--- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/backend/src/bitcoin_api.rs b/src/backend/src/bitcoin_api.rs index d83133d60..71618f029 100644 --- a/src/backend/src/bitcoin_api.rs +++ b/src/backend/src/bitcoin_api.rs @@ -11,12 +11,12 @@ use ic_cdk::api::management_canister::bitcoin::{ async fn get_utxos( network: BitcoinNetwork, address: String, - maybe_next_page: Option>, + filter: Option, ) -> Result { let utxos_res = bitcoin_get_utxos(GetUtxosRequest { address, network, - filter: maybe_next_page.map(UtxoFilter::Page), + filter, }) .await .map_err(|err| err.1)?; @@ -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, 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, +) -> Result, String> { + let filter = min_confirmations.map(UtxoFilter::MinConfirmations); + let mut utxos_response = get_utxos(network, address.clone(), filter).await?; let mut all_utxos: Vec = utxos_response.utxos; let mut next_page: Option> = 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; } diff --git a/src/backend/src/lib.rs b/src/backend/src/lib.rs index bab6a9604..90b77ba79 100644 --- a/src/backend/src/lib.rs +++ b/src/backend/src/lib.rs @@ -285,13 +285,19 @@ fn list_custom_tokens() -> Vec { 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 { - 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(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 From 5c54e805aa611e95622f1bea6c2cea269f3a0c9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lloren=C3=A7?= Date: Tue, 1 Oct 2024 14:37:38 +0200 Subject: [PATCH 2/2] Add param to SelectedUtxosFeeRequest --- src/backend/backend.did | 1 + src/backend/src/lib.rs | 6 +++++- src/backend/tests/it/bitcoin.rs | 2 ++ src/declarations/backend/backend.did | 1 + src/declarations/backend/backend.did.d.ts | 1 + src/declarations/backend/backend.factory.certified.did.js | 3 ++- src/declarations/backend/backend.factory.did.js | 3 ++- src/shared/src/types.rs | 1 + 8 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/backend/backend.did b/src/backend/backend.did index 424bbfe23..4b586a017 100644 --- a/src/backend/backend.did +++ b/src/backend/backend.did @@ -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; diff --git a/src/backend/src/lib.rs b/src/backend/src/lib.rs index 90b77ba79..ad216fc4b 100644 --- a/src/backend/src/lib.rs +++ b/src/backend/src/lib.rs @@ -294,7 +294,11 @@ async fn btc_select_user_utxos_fee( let all_utxos = bitcoin_api::get_all_utxos( params.network, params.source_address, - Some(MIN_CONFIRMATIONS_ACCEPTED_BTC_TX), + Some( + params + .min_confirmations + .unwrap_or(MIN_CONFIRMATIONS_ACCEPTED_BTC_TX), + ), ) .await .map_err(|msg| SelectedUtxosFeeError::InternalError { msg })?; diff --git a/src/backend/tests/it/bitcoin.rs b/src/backend/tests/it/bitcoin.rs index 65d3955f1..f303bfe8d 100644 --- a/src/backend/tests/it/bitcoin.rs +++ b/src/backend/tests/it/bitcoin.rs @@ -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::>( caller, diff --git a/src/declarations/backend/backend.did b/src/declarations/backend/backend.did index 424bbfe23..4b586a017 100644 --- a/src/declarations/backend/backend.did +++ b/src/declarations/backend/backend.did @@ -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; diff --git a/src/declarations/backend/backend.did.d.ts b/src/declarations/backend/backend.did.d.ts index bdb9a5175..ab73285f0 100644 --- a/src/declarations/backend/backend.did.d.ts +++ b/src/declarations/backend/backend.did.d.ts @@ -136,6 +136,7 @@ export interface SelectedUtxosFeeRequest { network: BitcoinNetwork; amount_satoshis: bigint; source_address: string; + min_confirmations: [] | [number]; } export interface SelectedUtxosFeeResponse { fee_satoshis: bigint; diff --git a/src/declarations/backend/backend.factory.certified.did.js b/src/declarations/backend/backend.factory.certified.did.js index 0a129666b..9e64cac33 100644 --- a/src/declarations/backend/backend.factory.certified.did.js +++ b/src/declarations/backend/backend.factory.certified.did.js @@ -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), diff --git a/src/declarations/backend/backend.factory.did.js b/src/declarations/backend/backend.factory.did.js index aeb2ad192..f39af9b73 100644 --- a/src/declarations/backend/backend.factory.did.js +++ b/src/declarations/backend/backend.factory.did.js @@ -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), diff --git a/src/shared/src/types.rs b/src/shared/src/types.rs index b0991e9ca..52278def0 100644 --- a/src/shared/src/types.rs +++ b/src/shared/src/types.rs @@ -170,6 +170,7 @@ pub mod bitcoin { pub amount_satoshis: u64, pub source_address: String, pub network: BitcoinNetwork, + pub min_confirmations: Option, } #[derive(CandidType, Deserialize, Clone, Eq, PartialEq, Debug)]