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::lightning::PaymentHash;
9use ark::offboard::OffboardRequest;
10use ark::tree::signed::UnlockHash;
11use ark::vtxo::VtxoPolicyKind;
12use bark::round::RoundFlowKind;
13
14#[cfg(feature = "utoipa")]
15use utoipa::ToSchema;
16
17use crate::cli::RoundStatus;
18
19
20/// Query parameters for filtering wallet history by payment method.
21///
22/// Both fields are optional but must be supplied together: omit both to get the
23/// full history, or provide both to filter by a single payment method. The pair
24/// mirrors the serialized form of a payment method (its `type` tag and `value`).
25#[derive(Default, Serialize, Deserialize)]
26#[cfg_attr(feature = "utoipa", derive(ToSchema))]
27pub struct HistoryQuery {
28	/// The payment method type tag to filter by, e.g. `ark`, `bitcoin`,
29	/// `output-script`, `invoice`, `offer`, `lightning-address`, `lnurl` or
30	/// `custom`. Must be provided together with `value`.
31	#[serde(rename = "type")]
32	pub method_type: Option<String>,
33	/// The payment method value to filter by, e.g. the destination address or
34	/// invoice. Must be provided together with `type`.
35	pub value: Option<String>,
36}
37
38/// Query parameters for fee estimates that only require an amount.
39#[derive(Serialize, Deserialize)]
40#[cfg_attr(feature = "utoipa", derive(ToSchema))]
41pub struct FeeEstimateQuery {
42	/// The amount in satoshis to estimate fees for
43	pub amount_sat: u64,
44}
45
46/// Query parameters for send-onchain fee estimates.
47#[derive(Serialize, Deserialize)]
48#[cfg_attr(feature = "utoipa", derive(ToSchema))]
49pub struct SendOnchainFeeEstimateQuery {
50	/// The amount in satoshis to send
51	pub amount_sat: u64,
52	/// The destination Bitcoin address
53	pub address: String,
54}
55
56/// Query parameters for offboard-all fee estimates.
57#[derive(Serialize, Deserialize)]
58#[cfg_attr(feature = "utoipa", derive(ToSchema))]
59pub struct OffboardAllFeeEstimateQuery {
60	/// The destination Bitcoin address
61	pub address: String,
62}
63
64/// Request body for estimating the fee of offboarding a specific set of VTXOs.
65#[derive(Serialize, Deserialize)]
66#[cfg_attr(feature = "utoipa", derive(ToSchema))]
67pub struct OffboardFeeEstimateRequest {
68	/// The destination Bitcoin address. The fee depends on its script type.
69	pub address: String,
70	/// The IDs of the VTXOs to offboard. Each is offboarded in full.
71	pub vtxos: Vec<String>,
72}
73
74/// A fee estimate for an Ark wallet operation.
75#[derive(Serialize, Deserialize)]
76#[cfg_attr(feature = "utoipa", derive(ToSchema))]
77pub struct FeeEstimateResponse {
78	/// The total amount including fees (in satoshis)
79	#[serde(rename = "gross_amount_sat", with = "bitcoin::amount::serde::as_sat")]
80	#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
81	pub gross_amount: Amount,
82	/// The fee portion (in satoshis)
83	#[serde(rename = "fee_sat", with = "bitcoin::amount::serde::as_sat")]
84	#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
85	pub fee: Amount,
86	/// The amount excluding fees (in satoshis). For sends, this is the amount
87	/// the recipient receives. For receives, this is the amount the user gets.
88	#[serde(rename = "net_amount_sat", with = "bitcoin::amount::serde::as_sat")]
89	#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
90	pub net_amount: Amount,
91	/// The VTXOs that would be spent for this operation
92	#[cfg_attr(feature = "utoipa", schema(value_type = Vec<String>))]
93	pub vtxos_spent: Vec<VtxoId>,
94}
95
96impl From<bark::FeeEstimate> for FeeEstimateResponse {
97	fn from(estimate: bark::FeeEstimate) -> Self {
98		FeeEstimateResponse {
99			gross_amount: estimate.gross_amount,
100			fee: estimate.fee,
101			net_amount: estimate.net_amount,
102			vtxos_spent: estimate.vtxos_spent,
103		}
104	}
105}
106
107/// Query parameters for emergency (unilateral) exit fee estimates.
108#[derive(Serialize, Deserialize)]
109#[cfg_attr(feature = "utoipa", derive(ToSchema))]
110pub struct EmergencyExitFeeEstimateQuery {
111	/// Comma-separated VTXO ids to estimate the exit for. When omitted, every spendable VTXO in
112	/// the wallet is used (i.e. exit the entire wallet).
113	pub vtxo_ids: Option<String>,
114	/// The fee rate to price the estimate at, in sat/vB (fractions allowed). Applied to both the
115	/// broadcast and claim legs. When omitted, the broadcast leg uses the current `fast` rate and
116	/// the claim leg the `regular` rate.
117	pub fee_rate_sat_per_vb: Option<f64>,
118	/// The destination address for the claim. Only affects the claim-fee weight; when omitted a
119	/// placeholder of the configured network is used.
120	pub destination: Option<String>,
121	/// Scales the broadcast leg to cover feerate movement and UTXO consolidation. Must be finite
122	/// and non-negative; defaults to 1.2.
123	pub fee_margin: Option<f64>,
124}
125
126/// A fee breakdown for unilaterally (emergency) exiting a set of VTXOs.
127#[derive(Debug, Clone, Serialize, Deserialize)]
128#[cfg_attr(feature = "utoipa", derive(ToSchema))]
129pub struct EmergencyExitFeeEstimateResponse {
130	/// The CPFP fees to broadcast every not-yet-confirmed exit transaction (in satoshis),
131	/// including the fee margin. Paid from confirmed on-chain funds; this is the on-chain balance
132	/// to fund the exit with at the chosen margin.
133	#[serde(rename = "exit_broadcast_fee_sat", with = "bitcoin::amount::serde::as_sat")]
134	#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
135	pub exit_broadcast_fee: Amount,
136	/// The fee for the single batched transaction that drains the matured exit outputs (in
137	/// satoshis). Paid later out of the recovered value.
138	#[serde(rename = "claim_fee_sat", with = "bitcoin::amount::serde::as_sat")]
139	#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
140	pub claim_fee: Amount,
141	/// The sum of the broadcast and claim fees (in satoshis).
142	#[serde(rename = "total_fee_sat", with = "bitcoin::amount::serde::as_sat")]
143	#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
144	pub total_fee: Amount,
145	/// The fee rate the exit-broadcast leg was priced at (sat/vB), before the fee margin. Unless
146	/// an explicit fee rate was supplied, the claim leg is priced separately at the chain's
147	/// `regular` rate.
148	pub fee_rate_sat_per_vb: f64,
149	/// The number of exit transactions that still need to be broadcast and CPFP-bumped.
150	pub txs_to_broadcast: usize,
151	/// Whether the wallet's on-chain balance covers `exit_broadcast_fee_sat`.
152	///
153	/// **Deprecated**: the estimate is a funding target and no longer consults
154	/// the wallet's funds. Compare `exit_broadcast_fee_sat` against the on-chain
155	/// balance instead. This field is still populated from the last synced
156	/// on-chain balance for backwards compatibility and will be removed in a
157	/// future release.
158	#[deprecated(note = "compare `exit_broadcast_fee` against the on-chain balance instead")]
159	#[serde(default)]
160	#[cfg_attr(feature = "utoipa", schema(required = true))]
161	pub fundable: bool,
162}
163
164impl EmergencyExitFeeEstimateResponse {
165	/// Builds the response from an estimate and the wallet's on-chain balance,
166	/// `None` when the wallet has no on-chain wallet to fund the exit from.
167	#[allow(deprecated)] // fundable kept for old clients
168	pub fn new(e: bark::exit::ExitFeeEstimate, onchain_balance: Option<Amount>) -> Self {
169		EmergencyExitFeeEstimateResponse {
170			total_fee: e.total(),
171			exit_broadcast_fee: e.exit_broadcast_fee,
172			claim_fee: e.claim_fee,
173			fee_rate_sat_per_vb: e.fee_rate.to_sat_per_kwu() as f64 / 250.0,
174			txs_to_broadcast: e.txs_to_broadcast,
175			fundable: onchain_balance.map_or(false, |b| b >= e.exit_broadcast_fee),
176		}
177	}
178}
179
180/// Mempool fee rates for on-chain transactions.
181#[derive(Serialize, Deserialize)]
182#[cfg_attr(feature = "utoipa", derive(ToSchema))]
183pub struct OnchainFeeRatesResponse {
184	/// Fee rate targeting ~1 block confirmation (sat/vB)
185	pub fast_sat_per_vb: u64,
186	/// Fee rate targeting ~3 block confirmation (sat/vB)
187	pub regular_sat_per_vb: u64,
188	/// Fee rate targeting ~6 block confirmation (sat/vB)
189	pub slow_sat_per_vb: u64,
190}
191
192
193#[derive(Serialize, Deserialize)]
194#[cfg_attr(feature = "utoipa", derive(ToSchema))]
195pub struct TipResponse {
196	pub tip_height: u32,
197}
198
199#[derive(Serialize, Deserialize)]
200#[cfg_attr(feature = "utoipa", derive(ToSchema))]
201pub struct MailboxSyncResponse {
202	/// The mailbox checkpoint (tip) the wallet has consumed up to after
203	/// the sync. Monotonically non-decreasing across successful syncs.
204	pub checkpoint: u64,
205}
206
207#[derive(Serialize, Deserialize)]
208#[cfg_attr(feature = "utoipa", derive(ToSchema))]
209pub struct CreateWalletRequest {
210	/// The Ark server to use for the wallet.
211	/// Optional when a config.toml already exists in the datadir or when
212	/// the network has a default Ark server (mainnet and signet).
213	pub ark_server: Option<String>,
214	/// An access token for a private Ark server.
215	///
216	/// **Deprecated**: access tokens are no longer enforced by the server;
217	/// this field will be removed in a future release.
218	#[deprecated(
219		since = "0.2.4",
220		note = "access tokens are not enforced by the server; this field will be removed",
221	)]
222	pub ark_server_access_token: Option<String>,
223	/// The chain source to use for the wallet.
224	/// Optional when a config.toml already exists in the datadir or when
225	/// the network has a default chain source (mainnet, signet and
226	/// mutinynet).
227	pub chain_source: Option<ChainSourceConfig>,
228	/// The optional mnemonic to use for the wallet
229	pub mnemonic: Option<String>,
230	/// The network to use for the wallet
231	pub network: BarkNetwork,
232	/// An optional birthday height to start syncing the wallet from
233	pub birthday_height: Option<u32>,
234	/// How many consecutive unused key indices a VTXO key scan may cross before
235	/// it concludes the wallet doesn't own a recovered/imported VTXO.
236	#[serde(default)]
237	#[cfg_attr(feature = "utoipa", schema(maximum = 100_000))]
238	pub gap_limit: Option<u32>,
239	/// Proceed even if the datadir contains unexpected files
240	#[serde(default)]
241	pub force: bool,
242}
243
244/// Networks bark can be used on
245#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
246#[serde(rename_all = "kebab-case")]
247#[cfg_attr(feature = "utoipa", derive(ToSchema))]
248pub enum BarkNetwork {
249	/// Bitcoin's mainnet
250	Mainnet,
251	/// The official Bitcoin Core signet
252	Signet,
253	/// Mutinynet
254	Mutinynet,
255	/// Any regtest network
256	Regtest,
257}
258
259#[derive(Serialize, Deserialize)]
260#[serde(rename_all = "kebab-case")]
261#[cfg_attr(feature = "utoipa", derive(ToSchema))]
262pub enum ChainSourceConfig {
263	/// Use a bitcoind RPC server
264	Bitcoind {
265		bitcoind: String,
266		bitcoind_auth: BitcoindAuth,
267	},
268	/// Use an Esplora HTTP server
269	Esplora {
270		url: String,
271	},
272}
273
274#[derive(Serialize, Deserialize)]
275#[serde(rename_all = "kebab-case")]
276#[cfg_attr(feature = "utoipa", derive(ToSchema))]
277pub enum BitcoindAuth {
278	/// Use a cookie file for authentication
279	Cookie {
280		cookie: String,
281	},
282	/// Use a username and password for authentication
283	UserPass {
284		user: String,
285		pass: String,
286	},
287}
288
289#[derive(Serialize, Deserialize)]
290#[cfg_attr(feature = "utoipa", derive(ToSchema))]
291pub struct CreateWalletResponse {
292	pub fingerprint: String,
293}
294
295#[derive(Serialize, Deserialize)]
296#[cfg_attr(feature = "utoipa", derive(ToSchema))]
297pub struct ConnectedResponse {
298	/// Whether the wallet is currently connected to its Ark server
299	pub connected: bool,
300}
301
302#[derive(Serialize, Deserialize)]
303#[cfg_attr(feature = "utoipa", derive(ToSchema))]
304pub struct MnemonicResponse {
305	/// The BIP-39 mnemonic phrase backing the wallet.
306	pub mnemonic: String,
307}
308
309#[derive(Serialize, Deserialize)]
310#[cfg_attr(feature = "utoipa", derive(ToSchema))]
311pub struct ArkAddressResponse {
312	#[cfg_attr(feature = "utoipa", schema(value_type = String))]
313	pub address: String,
314}
315
316/// Request to build a BIP 321 unified payment URI.
317///
318/// An Ark address is always included. A BOLT11 invoice is only included when
319/// `amount_sat` is given (an amount is required to create one). An on-chain
320/// address is included only when `onchain` is `true`.
321#[derive(Serialize, Deserialize)]
322#[cfg_attr(feature = "utoipa", derive(ToSchema))]
323pub struct Bip321UriRequest {
324	/// Optional amount (in satoshis) to request. When set, it is embedded in
325	/// the URI and used to create the BOLT11 invoice. Any server-configured
326	/// [LightningReceiveFees](crate::cli::fees::LightningReceiveFees) are
327	/// deducted from the amount the client ultimately receives over Lightning.
328	#[serde(default, skip_serializing_if = "Option::is_none")]
329	pub amount_sat: Option<u64>,
330	/// Whether to include a fresh on-chain address as a payment destination.
331	/// Defaults to `false`.
332	#[serde(default, skip_serializing_if = "Option::is_none")]
333	pub onchain: Option<bool>,
334	/// Optional label describing the payment, recorded in the URI's `label`.
335	#[serde(default, skip_serializing_if = "Option::is_none")]
336	pub label: Option<String>,
337	/// Optional message describing the payment, recorded in the URI's `message`.
338	#[serde(default, skip_serializing_if = "Option::is_none")]
339	pub message: Option<String>,
340}
341
342/// Query parameters for building a BIP 321 URI.
343#[derive(Serialize, Deserialize)]
344#[cfg_attr(feature = "utoipa", derive(ToSchema))]
345pub struct Bip321UriQuery {
346	/// Whether to upper-case the returned `bip321` URI so QR encoders can use
347	/// the compact alphanumeric mode. Defaults to `false`.
348	/// Requesting an upper-case URI fails when it carries case-sensitive data
349	/// (a label or message with lowercase characters, or base58 address) that
350	/// cannot be upper-cased.
351	pub uppercase: Option<bool>,
352}
353
354/// A BIP 321 unified payment URI together with its individual destinations.
355///
356/// The `bip321` field is the combined `bitcoin:` URI; the other fields expose
357/// each generated destination separately for convenience. A field is `null`
358/// when that destination was not requested or could not be produced.
359#[derive(Serialize, Deserialize)]
360#[cfg_attr(feature = "utoipa", derive(ToSchema))]
361pub struct Bip321UriResponse {
362	/// The generated Ark address, if any.
363	#[serde(skip_serializing_if = "Option::is_none")]
364	pub ark: Option<String>,
365	/// The generated BOLT11 invoice, included only when an amount was given.
366	#[serde(skip_serializing_if = "Option::is_none")]
367	pub bolt11: Option<String>,
368	/// The generated on-chain address, included only when `onchain` was set.
369	#[serde(skip_serializing_if = "Option::is_none")]
370	pub onchain: Option<String>,
371	/// The combined BIP 321 `bitcoin:` URI.
372	pub bip321: String,
373}
374
375/// Response for the encoded-VTXO endpoint.
376///
377/// Wraps the hex-encoded VTXO in a named field so clients can easily
378/// extract it.
379#[derive(Serialize, Deserialize)]
380#[cfg_attr(feature = "utoipa", derive(ToSchema))]
381pub struct EncodedVtxoResponse {
382	/// Hex-encoded serialized VTXO.
383	pub encoded: crate::primitives::EncodedVtxo,
384}
385
386#[derive(Serialize, Deserialize)]
387#[cfg_attr(feature = "utoipa", derive(ToSchema))]
388pub struct VtxosQuery {
389	/// Return all VTXOs regardless of their state (including spent ones)
390	pub all: Option<bool>,
391}
392
393#[derive(Serialize, Deserialize)]
394#[cfg_attr(feature = "utoipa", derive(ToSchema))]
395pub struct RefreshRequest {
396	/// List of VTXO IDs to refresh. The sum of the VTXOs being refreshed must be
397	/// >= [P2TR_DUST](bitcoin_ext::P2TR_DUST). Keep in mind that fees set out in
398	/// [RefreshFees](crate::cli::fees::RefreshFees) will be deducted from the newly created VTXO, this
399	/// value must also be >= [P2TR_DUST](bitcoin_ext::P2TR_DUST).
400	pub vtxos: Vec<String>,
401}
402
403#[derive(Serialize, Deserialize)]
404#[cfg_attr(feature = "utoipa", derive(ToSchema))]
405pub struct DelegatedRefreshRequest {
406	/// List of VTXO IDs to refresh. The sum of the VTXOs being refreshed must be
407	/// >= [P2TR_DUST](bitcoin_ext::P2TR_DUST). Keep in mind that fees set out in
408	/// [RefreshFees](crate::cli::fees::RefreshFees) will be deducted from the newly created VTXO, this
409	/// value must also be >= [P2TR_DUST](bitcoin_ext::P2TR_DUST).
410	pub vtxos: Vec<String>,
411	/// Optional block height to schedule the refresh at. When set, the refresh fee is priced at
412	/// that height and the server includes the participation in the first round once the chain
413	/// tip reaches it; when omitted, the participation is eligible for the next round.
414	pub height: Option<u32>,
415}
416
417#[derive(Serialize, Deserialize)]
418#[cfg_attr(feature = "utoipa", derive(ToSchema))]
419pub struct BoardRequest {
420	/// An amount of onchain funds to board (in satoshis). For a board operation to be successful,
421	/// this value, with any server-configured [BoardFees](crate::cli::fees::BoardFees) deducted, must be
422	/// >= [P2TR_DUST](bitcoin_ext::P2TR_DUST).
423	pub amount_sat: u64,
424}
425
426#[derive(Serialize, Deserialize)]
427#[cfg_attr(feature = "utoipa", derive(ToSchema))]
428pub struct SendRequest {
429	/// The destination can be an Ark address, a BOLT11-invoice, LNURL or a lightning address
430	pub destination: String,
431	/// The amount to send (in satoshis). Optional for bolt11 invoices. Depending on the
432	/// `destination`, the wallet must contain this amount plus any fees configured by the server in
433	/// [FeeSchedule](crate::cli::fees::FeeSchedule).
434	pub amount_sat: Option<u64>,
435	/// An optional comment, only supported when paying to lightning addresses
436	pub comment: Option<String>,
437	/// For lightning destinations, how many seconds the Ark server should keep
438	/// trying to pay, capped to the server's maximum. The server picks its
439	/// default when empty.
440	#[serde(default)]
441	pub retry_for_secs: Option<u64>,
442}
443
444#[derive(Serialize, Deserialize)]
445#[cfg_attr(feature = "utoipa", derive(ToSchema))]
446pub struct SendResponse {
447	/// Success message
448	pub message: String,
449	/// The payment hash, when the destination resolved to a lightning
450	/// payment. Can be used to poll the payment status.
451	#[serde(default, skip_serializing_if = "Option::is_none")]
452	#[cfg_attr(feature = "utoipa", schema(value_type = Option<String>))]
453	pub payment_hash: Option<PaymentHash>,
454}
455
456/// Request to sign an arbitrary message with one of the wallet's keys
457#[derive(Serialize, Deserialize)]
458#[cfg_attr(feature = "utoipa", derive(ToSchema))]
459pub struct SignMessageRequest {
460	/// The message to sign
461	pub message: String,
462	/// The Ark address to sign the message with
463	pub address: String,
464}
465
466/// Request to verify a signed message
467///
468/// Exactly one of `pubkey` and `address` must be set.
469#[derive(Serialize, Deserialize)]
470#[cfg_attr(feature = "utoipa", derive(ToSchema))]
471pub struct VerifyMessageRequest {
472	/// The message that was signed
473	pub message: String,
474	/// The BIP-340 Schnorr signature over the message digest
475	/// `SHA256("bark/message" || message)`, in hex
476	pub signature: String,
477	/// The public key to verify the signature against
478	pub pubkey: Option<String>,
479	/// The Ark address whose user public key to verify the signature against
480	pub address: Option<String>,
481}
482
483#[derive(Serialize, Deserialize)]
484#[cfg_attr(feature = "utoipa", derive(ToSchema))]
485pub struct SendOnchainRequest {
486	/// The destination Bitcoin address
487	pub destination: String,
488	/// The amount (in satoshis) to be received by `destination` onchain. Must be
489	/// >= [P2TR_DUST](bitcoin_ext::P2TR_DUST). Server-configured fees laid out in
490	/// [OffboardFees](crate::cli::fees::OffboardFees) will be added on top of this amount.
491	pub amount_sat: u64,
492}
493
494#[derive(Serialize, Deserialize)]
495#[cfg_attr(feature = "utoipa", derive(ToSchema))]
496pub struct OffboardVtxosRequest {
497	/// Optional Bitcoin address to send to. If not provided, uses the onchain wallet's address
498	pub address: Option<String>,
499	/// List of VTXO IDs to offboard. The sum of the VTXOs being refreshed must be
500	/// >= [P2TR_DUST](bitcoin_ext::P2TR_DUST) after the server-configured
501	/// [OffboardFees](crate::cli::fees::OffboardFees) are deducted.
502	pub vtxos: Vec<String>,
503}
504
505#[derive(Serialize, Deserialize)]
506#[cfg_attr(feature = "utoipa", derive(ToSchema))]
507pub struct OffboardAllRequest {
508	/// Optional Bitcoin address to send to. If not provided, uses the onchain wallet's address
509	pub address: Option<String>,
510}
511
512#[derive(Serialize, Deserialize)]
513#[cfg_attr(feature = "utoipa", derive(ToSchema))]
514pub struct ImportVtxoRequest {
515	/// Hex-encoded VTXOs to import
516	pub vtxos: Vec<String>,
517	/// How many consecutive unused key indices to scan for each VTXO's user
518	/// pubkey. Overrides the wallet's configured gap limit.
519	#[serde(default)]
520	#[cfg_attr(feature = "utoipa", schema(maximum = 100_000))]
521	pub gap_limit: Option<u32>,
522	/// Import as spendable without asking the server for each VTXO's state.
523	///
524	/// Use it when you already know it's spendable or when the server can't be reached,
525	/// as it can leave the wallet in an inconsistent state.
526	#[serde(default)]
527	pub skip_status_check: bool,
528	/// Keep the VTXOs that import successfully even when another one in the
529	/// request fails. The response lists the VTXOs that were kept.
530	///
531	/// Without it, a single failure discards the whole request.
532	#[serde(default)]
533	pub allow_partial: bool,
534}
535
536#[derive(Serialize, Deserialize)]
537#[cfg_attr(feature = "utoipa", derive(ToSchema))]
538pub struct LightningInvoiceRequest {
539	/// The amount to create invoice for (in satoshis). This is the amount the payee will pay but
540	/// the final amount received by the client will have any server-configured
541	/// [LightningReceiveFees](crate::cli::fees::LightningReceiveFees) deducted.
542	pub amount_sat: u64,
543	/// Optional description embedded in the invoice as its memo.
544	#[serde(default, skip_serializing_if = "Option::is_none")]
545	pub description: Option<String>,
546	/// Optional lightning receive token for authentication of the claim, if
547	/// the server requires one and there are no existing spendable VTXOs to
548	/// prove ownership of.
549	#[serde(default, skip_serializing_if = "Option::is_none")]
550	pub token: Option<String>,
551}
552
553#[derive(Serialize, Deserialize)]
554#[cfg_attr(feature = "utoipa", derive(ToSchema))]
555pub struct LightningInvoiceForAddressRequest {
556	/// The amount to create invoice for (in satoshis).
557	pub amount_sat: u64,
558	/// Ark address that will receive the claimed VTXO.
559	pub address: String,
560	/// Optional description embedded in the invoice as its memo.
561	#[serde(default, skip_serializing_if = "Option::is_none")]
562	pub description: Option<String>,
563}
564
565#[derive(Serialize, Deserialize)]
566#[cfg_attr(feature = "utoipa", derive(ToSchema))]
567pub struct LightningPayRequest {
568	/// The invoice, offer, or lightning address to pay
569	pub destination: String,
570	/// The amount to send (in satoshis). Optional for bolt11 invoices with amount. This must be
571	/// higher than the minimum fee laid out in server-configured
572	/// [LightningSendFees](crate::cli::fees::LightningSendFees). The wallet must also contain enough
573	/// funds to cover the amount plus any fees.
574	pub amount_sat: Option<u64>,
575	/// An optional comment, only supported when paying to lightning addresses
576	pub comment: Option<String>,
577	/// How many seconds the Ark server should keep trying to pay, capped to
578	/// the server's maximum. The server picks its default when empty.
579	#[serde(default)]
580	pub retry_for_secs: Option<u64>,
581}
582
583#[derive(Serialize, Deserialize)]
584#[cfg_attr(feature = "utoipa", derive(ToSchema))]
585pub struct LightningPayResponse {
586	/// Success message
587	pub message: String,
588	/// The payment hash of the lightning payment. Can be used to poll the
589	/// payment status.
590	#[serde(default, skip_serializing_if = "Option::is_none")]
591	#[cfg_attr(feature = "utoipa", schema(value_type = Option<String>))]
592	pub payment_hash: Option<PaymentHash>,
593}
594
595#[derive(Serialize, Deserialize)]
596#[cfg_attr(feature = "utoipa", derive(ToSchema))]
597pub struct OnchainSendRequest {
598	/// The destination Bitcoin address
599	pub destination: String,
600	/// The amount to send (in satoshis)
601	pub amount_sat: u64,
602}
603
604#[derive(Serialize, Deserialize)]
605#[cfg_attr(feature = "utoipa", derive(ToSchema))]
606pub struct OnchainSendManyRequest {
607	/// List of destinations in format "address:amount"
608	pub destinations: Vec<String>,
609	/// Sends the transaction immediately instead of waiting
610	pub immediate: Option<bool>,
611}
612
613#[derive(Serialize, Deserialize)]
614#[cfg_attr(feature = "utoipa", derive(ToSchema))]
615pub struct OnchainDrainRequest {
616	/// The destination Bitcoin address
617	pub destination: String,
618}
619
620#[derive(Serialize, Deserialize)]
621#[cfg_attr(feature = "utoipa", derive(ToSchema))]
622pub struct ExitStatusRequest {
623	/// Whether to include the detailed history of the exit process
624	pub history: Option<bool>,
625	/// Whether to include the exit transactions and their CPFP children
626	pub transactions: Option<bool>,
627}
628
629#[derive(Serialize, Deserialize)]
630#[cfg_attr(feature = "utoipa", derive(ToSchema))]
631pub struct ExitStartRequest {
632	/// The ID of VTXOs to unilaterally exit
633	pub vtxos: Vec<String>,
634}
635
636#[derive(Serialize, Deserialize)]
637#[cfg_attr(feature = "utoipa", derive(ToSchema))]
638pub struct ExitStartResponse {
639	pub message: String,
640}
641
642#[derive(Serialize, Deserialize)]
643#[cfg_attr(feature = "utoipa", derive(ToSchema))]
644pub struct ExitProgressRequest {
645	/// Wait until the exit is completed
646	pub wait: Option<bool>,
647	/// Sets the desired fee-rate in sats/kvB to use broadcasting exit transactions
648	pub fee_rate: Option<u64>,
649}
650
651#[derive(Serialize, Deserialize)]
652#[cfg_attr(feature = "utoipa", derive(ToSchema))]
653pub struct ExitClaimAllRequest {
654	/// The destination Bitcoin address
655	pub destination: String,
656	/// Sets the desired fee-rate in sats/kvB to use broadcasting exit transactions
657	pub fee_rate: Option<u64>,
658}
659
660#[derive(Serialize, Deserialize)]
661#[cfg_attr(feature = "utoipa", derive(ToSchema))]
662pub struct ExitClaimVtxosRequest {
663	/// The destination Bitcoin address
664	pub destination: String,
665	/// The ID of an exited VTXO to be claimed
666	pub vtxos: Vec<String>,
667	/// Sets the desired fee-rate in sats/kvB to use broadcasting exit transactions
668	pub fee_rate: Option<u64>,
669}
670
671#[derive(Serialize, Deserialize)]
672#[cfg_attr(feature = "utoipa", derive(ToSchema))]
673pub struct ExitClaimResponse {
674	pub message: String,
675}
676
677#[derive(Serialize, Deserialize)]
678#[cfg_attr(feature = "utoipa", derive(ToSchema))]
679pub struct ExitCancelResponse {
680	pub message: String,
681}
682
683
684#[derive(Serialize, Deserialize)]
685#[cfg_attr(feature = "utoipa", derive(ToSchema))]
686pub struct VtxoRequestInfo {
687	#[serde(rename = "amount_sat", with = "bitcoin::amount::serde::as_sat")]
688	#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
689	pub amount: Amount,
690	#[cfg_attr(feature = "utoipa", schema(value_type = String))]
691	pub policy_type: VtxoPolicyKind,
692	#[cfg_attr(feature = "utoipa", schema(value_type = String))]
693	pub user_pubkey: PublicKey,
694}
695
696impl<'a> From<&'a ark::VtxoRequest> for VtxoRequestInfo {
697	fn from(v: &'a ark::VtxoRequest) -> Self {
698		Self {
699			amount: v.amount,
700			policy_type: v.policy.policy_type(),
701			user_pubkey: v.policy.user_pubkey(),
702		}
703	}
704}
705
706#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
707#[cfg_attr(feature = "utoipa", derive(ToSchema))]
708pub struct OffboardRequestInfo {
709	/// hexadecimal representation of the output script
710	pub script_pubkey_hex: String,
711	/// opcode representation of the output script
712	pub script_pubkey_asm: String,
713	/// The target amount in sats.
714	#[serde(rename = "net_amount_sat", with = "bitcoin::amount::serde::as_sat")]
715	#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
716	pub net_amount: Amount,
717	/// Determines whether fees should be added onto the given amount or deducted from it.
718	pub deduct_fees_from_gross_amount: bool,
719	/// What fee rate was used when calculating the fee for the offboard.
720	#[serde(rename = "fee_rate_kwu")]
721	#[cfg_attr(feature = "utoipa", schema(value_type = u64))]
722	pub fee_rate: FeeRate,
723}
724
725impl<'a> From<&'a OffboardRequest> for OffboardRequestInfo {
726	fn from(v: &'a OffboardRequest) -> Self {
727		Self {
728			script_pubkey_hex: v.script_pubkey.to_hex_string(),
729			script_pubkey_asm: v.script_pubkey.to_asm_string(),
730			net_amount: v.net_amount,
731			deduct_fees_from_gross_amount: v.deduct_fees_from_gross_amount,
732			fee_rate: v.fee_rate,
733		}
734	}
735}
736
737#[derive(Serialize, Deserialize)]
738#[cfg_attr(feature = "utoipa", derive(ToSchema))]
739pub struct RoundParticipationInfo {
740	#[cfg_attr(feature = "utoipa", schema(value_type = Vec<String>))]
741	pub inputs: Vec<VtxoId>,
742	pub outputs: Vec<VtxoRequestInfo>,
743}
744
745impl<'a> From<&'a bark::round::RoundParticipation> for RoundParticipationInfo {
746	fn from(v: &'a bark::round::RoundParticipation) -> Self {
747		Self {
748			inputs: v.inputs.iter().map(|v| v.id()).collect(),
749			outputs: v.outputs.iter().map(Into::into).collect(),
750		}
751	}
752}
753
754/// Lifecycle phase of a round participation.
755#[derive(Serialize, Deserialize)]
756#[cfg_attr(feature = "utoipa", derive(ToSchema))]
757#[serde(rename_all = "kebab-case")]
758pub enum RoundFlowState {
759	/// Delegated participation waiting for its round.
760	DelegatedPending,
761	/// Interactive participation waiting for its round.
762	Pending,
763	/// The interactive part is being played out with the server.
764	Ongoing,
765	/// The round finished and its funding tx is waiting for confirmations.
766	AwaitingConfirmations,
767	/// The participation failed.
768	Failed,
769	/// The user canceled the participation.
770	Canceled,
771}
772
773impl From<RoundFlowKind> for RoundFlowState {
774	fn from(kind: RoundFlowKind) -> Self {
775		match kind {
776			RoundFlowKind::DelegatedPending => Self::DelegatedPending,
777			RoundFlowKind::Pending => Self::Pending,
778			RoundFlowKind::Ongoing => Self::Ongoing,
779			RoundFlowKind::AwaitingConfirmations => Self::AwaitingConfirmations,
780			RoundFlowKind::Failed => Self::Failed,
781			RoundFlowKind::Canceled => Self::Canceled,
782		}
783	}
784}
785
786#[derive(Serialize, Deserialize)]
787#[cfg_attr(feature = "utoipa", derive(ToSchema))]
788pub struct PendingRoundInfo {
789	/// Unique identifier for the round
790	pub id: u32,
791	/// the current status of the round
792	pub status: RoundStatus,
793	/// Lifecycle phase of the participation
794	pub state: RoundFlowState,
795	/// the round participation details
796	pub participation: RoundParticipationInfo,
797	#[cfg_attr(feature = "utoipa", schema(value_type = String, nullable = true))]
798	pub unlock_hash: Option<UnlockHash>,
799	/// The block height a delegated participation is scheduled for, if any
800	#[serde(default)]
801	pub scheduled_height: Option<u32>,
802	/// The round transaction id, if already assigned
803	#[cfg_attr(feature = "utoipa", schema(value_type = String, nullable = true))]
804	pub funding_txid: Option<Txid>,
805	pub funding_tx_hex: Option<String>,
806}
807
808impl PendingRoundInfo {
809	pub fn new<G>(
810		state: &bark::persist::models::StoredRoundState<G>,
811		sync_result: anyhow::Result<bark::round::RoundStatus>,
812	) -> Self {
813		let funding_tx = state.state().funding_tx();
814		Self {
815			id: state.id().0,
816			status: match sync_result {
817				Ok(status) => status.into(),
818				Err(e) => RoundStatus::SyncError {
819					error: format!("{:#}", e),
820				},
821			},
822			state: state.state().flow_kind().into(),
823			participation: state.state().participation().into(),
824			unlock_hash: state.state().unlock_hash(),
825			scheduled_height: state.state().scheduled_height().map(Into::into),
826			funding_txid: funding_tx.map(|t| t.compute_txid()),
827			funding_tx_hex: funding_tx.map(|t| serialize_hex(t)),
828		}
829	}
830
831	/// Like [PendingRoundInfo::new], but without contacting the server: the
832	/// status is derived from the stored state alone.
833	pub fn from_state<G>(state: &bark::persist::models::StoredRoundState<G>) -> Self {
834		let status = match state.state().flow_kind() {
835			RoundFlowKind::AwaitingConfirmations => {
836				bark::round::RoundStatus::Unconfirmed {
837					funding_txid: state.state().funding_tx()
838						.expect("finished rounds have a funding tx")
839						.compute_txid(),
840				}
841			},
842			RoundFlowKind::Canceled => bark::round::RoundStatus::Canceled,
843			RoundFlowKind::DelegatedPending
844				| RoundFlowKind::Pending
845				| RoundFlowKind::Ongoing
846				| RoundFlowKind::Failed
847			=> bark::round::RoundStatus::Pending,
848		};
849		Self::new(state, Ok(status))
850	}
851}
852
853#[derive(Serialize, Deserialize)]
854#[cfg_attr(feature = "utoipa", derive(ToSchema))]
855pub struct WalletExistsResponse {
856	pub fingerprint: Option<String>,
857}
858
859#[derive(Serialize, Deserialize)]
860#[cfg_attr(feature = "utoipa", derive(ToSchema))]
861pub struct WalletDeleteRequest {
862	pub dangerous: bool,
863	pub fingerprint: String,
864}
865
866#[derive(Serialize, Deserialize)]
867#[cfg_attr(feature = "utoipa", derive(ToSchema))]
868pub struct WalletDeleteResponse {
869	pub deleted: bool,
870	pub fingerprint: Option<String>,
871	pub message: String,
872}