Skip to main content

bark_json/
web.rs

1
2use bitcoin::{Amount, FeeRate, Txid};
3use bitcoin::consensus::encode::serialize_hex;
4use bitcoin::secp256k1::PublicKey;
5use serde::{Deserialize, Serialize};
6
7use ark::VtxoId;
8use ark::offboard::OffboardRequest;
9use ark::tree::signed::UnlockHash;
10use ark::vtxo::VtxoPolicyKind;
11
12#[cfg(feature = "utoipa")]
13use utoipa::ToSchema;
14
15use crate::cli::RoundStatus;
16
17
18/// Query parameters for fee estimates that only require an amount.
19#[derive(Serialize, Deserialize)]
20#[cfg_attr(feature = "utoipa", derive(ToSchema))]
21pub struct FeeEstimateQuery {
22	/// The amount in satoshis to estimate fees for
23	pub amount_sat: u64,
24}
25
26/// Query parameters for send-onchain fee estimates.
27#[derive(Serialize, Deserialize)]
28#[cfg_attr(feature = "utoipa", derive(ToSchema))]
29pub struct SendOnchainFeeEstimateQuery {
30	/// The amount in satoshis to send
31	pub amount_sat: u64,
32	/// The destination Bitcoin address
33	pub address: String,
34}
35
36/// Query parameters for offboard-all fee estimates.
37#[derive(Serialize, Deserialize)]
38#[cfg_attr(feature = "utoipa", derive(ToSchema))]
39pub struct OffboardAllFeeEstimateQuery {
40	/// The destination Bitcoin address
41	pub address: String,
42}
43
44/// A fee estimate for an Ark wallet operation.
45#[derive(Serialize, Deserialize)]
46#[cfg_attr(feature = "utoipa", derive(ToSchema))]
47pub struct FeeEstimateResponse {
48	/// The total amount including fees (in satoshis)
49	#[serde(rename = "gross_amount_sat", with = "bitcoin::amount::serde::as_sat")]
50	#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
51	pub gross_amount: Amount,
52	/// The fee portion (in satoshis)
53	#[serde(rename = "fee_sat", with = "bitcoin::amount::serde::as_sat")]
54	#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
55	pub fee: Amount,
56	/// The amount excluding fees (in satoshis). For sends, this is the amount
57	/// the recipient receives. For receives, this is the amount the user gets.
58	#[serde(rename = "net_amount_sat", with = "bitcoin::amount::serde::as_sat")]
59	#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
60	pub net_amount: Amount,
61	/// The VTXOs that would be spent for this operation
62	#[cfg_attr(feature = "utoipa", schema(value_type = Vec<String>))]
63	pub vtxos_spent: Vec<VtxoId>,
64}
65
66impl From<bark::FeeEstimate> for FeeEstimateResponse {
67	fn from(estimate: bark::FeeEstimate) -> Self {
68		FeeEstimateResponse {
69			gross_amount: estimate.gross_amount,
70			fee: estimate.fee,
71			net_amount: estimate.net_amount,
72			vtxos_spent: estimate.vtxos_spent,
73		}
74	}
75}
76
77/// Mempool fee rates for on-chain transactions.
78#[derive(Serialize, Deserialize)]
79#[cfg_attr(feature = "utoipa", derive(ToSchema))]
80pub struct OnchainFeeRatesResponse {
81	/// Fee rate targeting ~1 block confirmation (sat/vB)
82	pub fast_sat_per_vb: u64,
83	/// Fee rate targeting ~3 block confirmation (sat/vB)
84	pub regular_sat_per_vb: u64,
85	/// Fee rate targeting ~6 block confirmation (sat/vB)
86	pub slow_sat_per_vb: u64,
87}
88
89
90#[derive(Serialize, Deserialize)]
91#[cfg_attr(feature = "utoipa", derive(ToSchema))]
92pub struct TipResponse {
93	pub tip_height: u32,
94}
95
96#[derive(Serialize, Deserialize)]
97#[cfg_attr(feature = "utoipa", derive(ToSchema))]
98pub struct CreateWalletRequest {
99	/// The Ark server to use for the wallet
100	pub ark_server: String,
101	/// The chain source to use for the wallet
102	pub chain_source: ChainSourceConfig,
103	/// The optional mnemonic to use for the wallet
104	pub mnemonic: Option<String>,
105	/// The network to use for the wallet
106	pub network: BarkNetwork,
107	/// An optional birthday height to start syncing the wallet from
108	pub birthday_height: Option<u32>,
109}
110
111/// Networks bark can be used on
112#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
113#[serde(rename_all = "kebab-case")]
114#[cfg_attr(feature = "utoipa", derive(ToSchema))]
115pub enum BarkNetwork {
116	/// Bitcoin's mainnet
117	Mainnet,
118	/// The official Bitcoin Core signet
119	Signet,
120	/// Mutinynet
121	Mutinynet,
122	/// Any regtest network
123	Regtest,
124}
125
126#[derive(Serialize, Deserialize)]
127#[serde(rename_all = "kebab-case")]
128#[cfg_attr(feature = "utoipa", derive(ToSchema))]
129pub enum ChainSourceConfig {
130	/// Use a bitcoind RPC server
131	Bitcoind {
132		bitcoind: String,
133		bitcoind_auth: BitcoindAuth,
134	},
135	/// Use an Esplora HTTP server
136	Esplora {
137		url: String,
138	},
139}
140
141#[derive(Serialize, Deserialize)]
142#[serde(rename_all = "kebab-case")]
143#[cfg_attr(feature = "utoipa", derive(ToSchema))]
144pub enum BitcoindAuth {
145	/// Use a cookie file for authentication
146	Cookie {
147		cookie: String,
148	},
149	/// Use a username and password for authentication
150	UserPass {
151		user: String,
152		pass: String,
153	},
154}
155
156#[derive(Serialize, Deserialize)]
157#[cfg_attr(feature = "utoipa", derive(ToSchema))]
158pub struct CreateWalletResponse {
159	pub fingerprint: String,
160}
161
162#[derive(Serialize, Deserialize)]
163#[cfg_attr(feature = "utoipa", derive(ToSchema))]
164pub struct ConnectedResponse {
165	/// Whether the wallet is currently connected to its Ark server
166	pub connected: bool,
167}
168
169#[derive(Serialize, Deserialize)]
170#[cfg_attr(feature = "utoipa", derive(ToSchema))]
171pub struct ArkAddressResponse {
172	#[cfg_attr(feature = "utoipa", schema(value_type = String))]
173	pub address: String,
174}
175
176/// Response for the encoded-VTXO endpoint.
177///
178/// Wraps the hex-encoded VTXO in a named field so clients can easily
179/// extract it.
180#[derive(Serialize, Deserialize)]
181#[cfg_attr(feature = "utoipa", derive(ToSchema))]
182pub struct EncodedVtxoResponse {
183	/// Hex-encoded serialized VTXO.
184	pub encoded: crate::primitives::EncodedVtxo,
185}
186
187#[derive(Serialize, Deserialize)]
188#[cfg_attr(feature = "utoipa", derive(ToSchema))]
189pub struct VtxosQuery {
190	/// Return all VTXOs regardless of their state (including spent ones)
191	pub all: Option<bool>,
192}
193
194#[derive(Serialize, Deserialize)]
195#[cfg_attr(feature = "utoipa", derive(ToSchema))]
196pub struct RefreshRequest {
197	/// List of VTXO IDs to refresh. The sum of the VTXOs being refreshed must be
198	/// >= [P2TR_DUST](bitcoin_ext::P2TR_DUST). Keep in mind that fees set out in
199	/// [RefreshFees](crate::cli::RefreshFees) will be deducted from the newly created VTXO, this
200	/// value must also be >= [P2TR_DUST](bitcoin_ext::P2TR_DUST).
201	pub vtxos: Vec<String>,
202}
203
204#[derive(Serialize, Deserialize)]
205#[cfg_attr(feature = "utoipa", derive(ToSchema))]
206pub struct BoardRequest {
207	/// An amount of onchain funds to board (in satoshis). For a board operation to be successful,
208	/// this value, with any server-configured [BoardFees](crate::cli::BoardFees) deducted, must be
209	/// >= [P2TR_DUST](bitcoin_ext::P2TR_DUST).
210	pub amount_sat: u64,
211}
212
213#[derive(Serialize, Deserialize)]
214#[cfg_attr(feature = "utoipa", derive(ToSchema))]
215pub struct SendRequest {
216	/// The destination can be an Ark address, a BOLT11-invoice, LNURL or a lightning address
217	pub destination: String,
218	/// The amount to send (in satoshis). Optional for bolt11 invoices. Depending on the
219	/// `destination`, the wallet must contain this amount plus any fees configured by the server in
220	/// [FeeSchedule](crate::cli::FeeSchedule).
221	pub amount_sat: Option<u64>,
222	/// An optional comment, only supported when paying to lightning addresses
223	pub comment: Option<String>,
224}
225
226#[derive(Serialize, Deserialize)]
227#[cfg_attr(feature = "utoipa", derive(ToSchema))]
228pub struct SendResponse {
229	/// Success message
230	pub message: String,
231}
232
233#[derive(Serialize, Deserialize)]
234#[cfg_attr(feature = "utoipa", derive(ToSchema))]
235pub struct SendOnchainRequest {
236	/// The destination Bitcoin address
237	pub destination: String,
238	/// The amount (in satoshis) to be received by `destination` onchain. Must be
239	/// >= [P2TR_DUST](bitcoin_ext::P2TR_DUST). Server-configured fees laid out in
240	/// [OffboardFees](crate::cli::OffboardFees) will be added on top of this amount.
241	pub amount_sat: u64,
242}
243
244#[derive(Serialize, Deserialize)]
245#[cfg_attr(feature = "utoipa", derive(ToSchema))]
246pub struct OffboardVtxosRequest {
247	/// Optional Bitcoin address to send to. If not provided, uses the onchain wallet's address
248	pub address: Option<String>,
249	/// List of VTXO IDs to offboard. The sum of the VTXOs being refreshed must be
250	/// >= [P2TR_DUST](bitcoin_ext::P2TR_DUST) after the server-configured
251	/// [OffboardFees](crate::cli::OffboardFees) are deducted.
252	pub vtxos: Vec<String>,
253}
254
255#[derive(Serialize, Deserialize)]
256#[cfg_attr(feature = "utoipa", derive(ToSchema))]
257pub struct OffboardAllRequest {
258	/// Optional Bitcoin address to send to. If not provided, uses the onchain wallet's address
259	pub address: Option<String>,
260}
261
262#[derive(Serialize, Deserialize)]
263#[cfg_attr(feature = "utoipa", derive(ToSchema))]
264pub struct ImportVtxoRequest {
265	/// Hex-encoded VTXOs to import
266	pub vtxos: Vec<String>,
267}
268
269#[derive(Serialize, Deserialize)]
270#[cfg_attr(feature = "utoipa", derive(ToSchema))]
271pub struct LightningInvoiceRequest {
272	/// The amount to create invoice for (in satoshis). This is the amount the payee will pay but
273	/// the final amount received by the client will have any server-configured
274	/// [LightningReceiveFees](crate::cli::LightningReceiveFees) deducted.
275	pub amount_sat: u64,
276}
277
278#[derive(Serialize, Deserialize)]
279#[cfg_attr(feature = "utoipa", derive(ToSchema))]
280pub struct LightningPayRequest {
281	/// The invoice, offer, or lightning address to pay
282	pub destination: String,
283	/// The amount to send (in satoshis). Optional for bolt11 invoices with amount. This must be
284	/// higher than the minimum fee laid out in server-configured
285	/// [LightningSendFees](crate::cli::LightningSendFees). The wallet must also contain enough
286	/// funds to cover the amount plus any fees.
287	pub amount_sat: Option<u64>,
288	/// An optional comment, only supported when paying to lightning addresses
289	pub comment: Option<String>,
290}
291
292#[derive(Serialize, Deserialize)]
293#[cfg_attr(feature = "utoipa", derive(ToSchema))]
294pub struct LightningPayResponse {
295	/// Success message
296	pub message: String,
297}
298
299#[derive(Serialize, Deserialize)]
300#[cfg_attr(feature = "utoipa", derive(ToSchema))]
301pub struct OnchainSendRequest {
302	/// The destination Bitcoin address
303	pub destination: String,
304	/// The amount to send (in satoshis)
305	pub amount_sat: u64,
306}
307
308#[derive(Serialize, Deserialize)]
309#[cfg_attr(feature = "utoipa", derive(ToSchema))]
310pub struct OnchainSendManyRequest {
311	/// List of destinations in format "address:amount"
312	pub destinations: Vec<String>,
313	/// Sends the transaction immediately instead of waiting
314	pub immediate: Option<bool>,
315}
316
317#[derive(Serialize, Deserialize)]
318#[cfg_attr(feature = "utoipa", derive(ToSchema))]
319pub struct OnchainDrainRequest {
320	/// The destination Bitcoin address
321	pub destination: String,
322}
323
324#[derive(Serialize, Deserialize)]
325#[cfg_attr(feature = "utoipa", derive(ToSchema))]
326pub struct ExitStatusRequest {
327	/// Whether to include the detailed history of the exit process
328	pub history: Option<bool>,
329	/// Whether to include the exit transactions and their CPFP children
330	pub transactions: Option<bool>,
331}
332
333#[derive(Serialize, Deserialize)]
334#[cfg_attr(feature = "utoipa", derive(ToSchema))]
335pub struct ExitStartRequest {
336	/// The ID of VTXOs to unilaterally exit
337	pub vtxos: Vec<String>,
338}
339
340#[derive(Serialize, Deserialize)]
341#[cfg_attr(feature = "utoipa", derive(ToSchema))]
342pub struct ExitStartResponse {
343	pub message: String,
344}
345
346#[derive(Serialize, Deserialize)]
347#[cfg_attr(feature = "utoipa", derive(ToSchema))]
348pub struct ExitProgressRequest {
349	/// Wait until the exit is completed
350	pub wait: Option<bool>,
351	/// Sets the desired fee-rate in sats/kvB to use broadcasting exit transactions
352	pub fee_rate: Option<u64>,
353}
354
355#[derive(Serialize, Deserialize)]
356#[cfg_attr(feature = "utoipa", derive(ToSchema))]
357pub struct ExitClaimAllRequest {
358	/// The destination Bitcoin address
359	pub destination: String,
360	/// Sets the desired fee-rate in sats/kvB to use broadcasting exit transactions
361	pub fee_rate: Option<u64>,
362}
363
364#[derive(Serialize, Deserialize)]
365#[cfg_attr(feature = "utoipa", derive(ToSchema))]
366pub struct ExitClaimVtxosRequest {
367	/// The destination Bitcoin address
368	pub destination: String,
369	/// The ID of an exited VTXO to be claimed
370	pub vtxos: Vec<String>,
371	/// Sets the desired fee-rate in sats/kvB to use broadcasting exit transactions
372	pub fee_rate: Option<u64>,
373}
374
375#[derive(Serialize, Deserialize)]
376#[cfg_attr(feature = "utoipa", derive(ToSchema))]
377pub struct ExitClaimResponse {
378	pub message: String,
379}
380
381
382#[derive(Serialize, Deserialize)]
383#[cfg_attr(feature = "utoipa", derive(ToSchema))]
384pub struct VtxoRequestInfo {
385	#[serde(rename = "amount_sat", with = "bitcoin::amount::serde::as_sat")]
386	#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
387	pub amount: Amount,
388	#[cfg_attr(feature = "utoipa", schema(value_type = String))]
389	pub policy_type: VtxoPolicyKind,
390	#[cfg_attr(feature = "utoipa", schema(value_type = String))]
391	pub user_pubkey: PublicKey,
392}
393
394impl<'a> From<&'a ark::VtxoRequest> for VtxoRequestInfo {
395	fn from(v: &'a ark::VtxoRequest) -> Self {
396		Self {
397			amount: v.amount,
398			policy_type: v.policy.policy_type(),
399			user_pubkey: v.policy.user_pubkey(),
400		}
401	}
402}
403
404#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
405#[cfg_attr(feature = "utoipa", derive(ToSchema))]
406pub struct OffboardRequestInfo {
407	/// hexadecimal representation of the output script
408	pub script_pubkey_hex: String,
409	/// opcode representation of the output script
410	pub script_pubkey_asm: String,
411	/// The target amount in sats.
412	#[serde(rename = "net_amount_sat", with = "bitcoin::amount::serde::as_sat")]
413	#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
414	pub net_amount: Amount,
415	/// Determines whether fees should be added onto the given amount or deducted from it.
416	pub deduct_fees_from_gross_amount: bool,
417	/// What fee rate was used when calculating the fee for the offboard.
418	#[serde(rename = "fee_rate_kwu")]
419	#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
420	pub fee_rate: FeeRate,
421}
422
423impl<'a> From<&'a OffboardRequest> for OffboardRequestInfo {
424	fn from(v: &'a OffboardRequest) -> Self {
425		Self {
426			script_pubkey_hex: v.script_pubkey.to_hex_string(),
427			script_pubkey_asm: v.script_pubkey.to_asm_string(),
428			net_amount: v.net_amount,
429			deduct_fees_from_gross_amount: v.deduct_fees_from_gross_amount,
430			fee_rate: v.fee_rate,
431		}
432	}
433}
434
435#[derive(Serialize, Deserialize)]
436#[cfg_attr(feature = "utoipa", derive(ToSchema))]
437pub struct RoundParticipationInfo {
438	#[cfg_attr(feature = "utoipa", schema(value_type = Vec<String>))]
439	pub inputs: Vec<VtxoId>,
440	pub outputs: Vec<VtxoRequestInfo>,
441}
442
443impl<'a> From<&'a bark::round::RoundParticipation> for RoundParticipationInfo {
444	fn from(v: &'a bark::round::RoundParticipation) -> Self {
445		Self {
446			inputs: v.inputs.iter().map(|v| v.id()).collect(),
447			outputs: v.outputs.iter().map(Into::into).collect(),
448		}
449	}
450}
451
452#[derive(Serialize, Deserialize)]
453#[cfg_attr(feature = "utoipa", derive(ToSchema))]
454pub struct PendingRoundInfo {
455	/// Unique identifier for the round
456	pub id: u32,
457	/// the current status of the round
458	pub status: RoundStatus,
459	/// the round participation details
460	pub participation: RoundParticipationInfo,
461	#[cfg_attr(feature = "utoipa", schema(value_type = String, nullable = true))]
462	pub unlock_hash: Option<UnlockHash>,
463	/// The round transaction id, if already assigned
464	#[cfg_attr(feature = "utoipa", schema(value_type = String, nullable = true))]
465	pub funding_txid: Option<Txid>,
466	pub funding_tx_hex: Option<String>,
467}
468
469impl PendingRoundInfo {
470	pub fn new<'a>(
471		state: &'a bark::persist::models::StoredRoundState,
472		sync_result: anyhow::Result<bark::round::RoundStatus>,
473	) -> Self {
474		let funding_tx = state.state().funding_tx();
475		Self {
476			id: state.id().0,
477			status: match sync_result {
478				Ok(status) => status.into(),
479				Err(e) => RoundStatus::SyncError {
480					error: format!("{:#}", e),
481				},
482			},
483			participation: state.state().participation().into(),
484			unlock_hash: state.state().unlock_hash(),
485			funding_txid: funding_tx.map(|t| t.compute_txid()),
486			funding_tx_hex: funding_tx.map(|t| serialize_hex(t)),
487		}
488	}
489}
490
491#[derive(Serialize, Deserialize)]
492#[cfg_attr(feature = "utoipa", derive(ToSchema))]
493pub struct WalletExistsResponse {
494	pub fingerprint: Option<String>,
495}
496
497#[derive(Serialize, Deserialize)]
498#[cfg_attr(feature = "utoipa", derive(ToSchema))]
499pub struct WalletDeleteRequest {
500	pub dangerous: bool,
501	pub fingerprint: String,
502}
503
504#[derive(Serialize, Deserialize)]
505#[cfg_attr(feature = "utoipa", derive(ToSchema))]
506pub struct WalletDeleteResponse {
507	pub deleted: bool,
508	pub message: String,
509}
510