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