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