mint.rs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753
  1. //! Mint types
  2. use std::fmt;
  3. use std::str::FromStr;
  4. use bitcoin::bip32::DerivationPath;
  5. use cashu::quote_id::QuoteId;
  6. use cashu::util::unix_time;
  7. use cashu::{
  8. Bolt11Invoice, MeltOptions, MeltQuoteBolt11Response, MintQuoteBolt11Response,
  9. MintQuoteBolt12Response, PaymentMethod,
  10. };
  11. use lightning::offers::offer::Offer;
  12. use serde::{Deserialize, Serialize};
  13. use tracing::instrument;
  14. use uuid::Uuid;
  15. use crate::nuts::{MeltQuoteState, MintQuoteState};
  16. use crate::payment::PaymentIdentifier;
  17. use crate::{Amount, CurrencyUnit, Error, Id, KeySetInfo, PublicKey};
  18. /// Operation kind for saga persistence
  19. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
  20. #[serde(rename_all = "lowercase")]
  21. pub enum OperationKind {
  22. /// Swap operation
  23. Swap,
  24. /// Mint operation
  25. Mint,
  26. /// Melt operation
  27. Melt,
  28. }
  29. impl fmt::Display for OperationKind {
  30. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  31. match self {
  32. OperationKind::Swap => write!(f, "swap"),
  33. OperationKind::Mint => write!(f, "mint"),
  34. OperationKind::Melt => write!(f, "melt"),
  35. }
  36. }
  37. }
  38. impl FromStr for OperationKind {
  39. type Err = Error;
  40. fn from_str(value: &str) -> Result<Self, Self::Err> {
  41. let value = value.to_lowercase();
  42. match value.as_str() {
  43. "swap" => Ok(OperationKind::Swap),
  44. "mint" => Ok(OperationKind::Mint),
  45. "melt" => Ok(OperationKind::Melt),
  46. _ => Err(Error::Custom(format!("Invalid operation kind: {value}"))),
  47. }
  48. }
  49. }
  50. /// States specific to swap saga
  51. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
  52. #[serde(rename_all = "snake_case")]
  53. pub enum SwapSagaState {
  54. /// Swap setup complete (proofs added, blinded messages added)
  55. SetupComplete,
  56. /// Outputs signed (signatures generated but not persisted)
  57. Signed,
  58. }
  59. impl fmt::Display for SwapSagaState {
  60. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  61. match self {
  62. SwapSagaState::SetupComplete => write!(f, "setup_complete"),
  63. SwapSagaState::Signed => write!(f, "signed"),
  64. }
  65. }
  66. }
  67. impl FromStr for SwapSagaState {
  68. type Err = Error;
  69. fn from_str(value: &str) -> Result<Self, Self::Err> {
  70. let value = value.to_lowercase();
  71. match value.as_str() {
  72. "setup_complete" => Ok(SwapSagaState::SetupComplete),
  73. "signed" => Ok(SwapSagaState::Signed),
  74. _ => Err(Error::Custom(format!("Invalid swap saga state: {value}"))),
  75. }
  76. }
  77. }
  78. /// States specific to melt saga
  79. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
  80. #[serde(rename_all = "snake_case")]
  81. pub enum MeltSagaState {
  82. /// Setup complete (proofs reserved, quote verified)
  83. SetupComplete,
  84. /// Payment sent to Lightning network
  85. PaymentSent,
  86. }
  87. impl fmt::Display for MeltSagaState {
  88. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  89. match self {
  90. MeltSagaState::SetupComplete => write!(f, "setup_complete"),
  91. MeltSagaState::PaymentSent => write!(f, "payment_sent"),
  92. }
  93. }
  94. }
  95. impl FromStr for MeltSagaState {
  96. type Err = Error;
  97. fn from_str(value: &str) -> Result<Self, Self::Err> {
  98. let value = value.to_lowercase();
  99. match value.as_str() {
  100. "setup_complete" => Ok(MeltSagaState::SetupComplete),
  101. "payment_sent" => Ok(MeltSagaState::PaymentSent),
  102. _ => Err(Error::Custom(format!("Invalid melt saga state: {}", value))),
  103. }
  104. }
  105. }
  106. /// Saga state for different operation types
  107. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
  108. #[serde(tag = "type", rename_all = "snake_case")]
  109. pub enum SagaStateEnum {
  110. /// Swap saga states
  111. Swap(SwapSagaState),
  112. /// Melt saga states
  113. Melt(MeltSagaState),
  114. // Future: Mint saga states
  115. // Mint(MintSagaState),
  116. }
  117. impl SagaStateEnum {
  118. /// Create from string given operation kind
  119. pub fn new(operation_kind: OperationKind, s: &str) -> Result<Self, Error> {
  120. match operation_kind {
  121. OperationKind::Swap => Ok(SagaStateEnum::Swap(SwapSagaState::from_str(s)?)),
  122. OperationKind::Melt => Ok(SagaStateEnum::Melt(MeltSagaState::from_str(s)?)),
  123. OperationKind::Mint => Err(Error::Custom("Mint saga not implemented yet".to_string())),
  124. }
  125. }
  126. /// Get string representation of the state
  127. pub fn state(&self) -> &str {
  128. match self {
  129. SagaStateEnum::Swap(state) => match state {
  130. SwapSagaState::SetupComplete => "setup_complete",
  131. SwapSagaState::Signed => "signed",
  132. },
  133. SagaStateEnum::Melt(state) => match state {
  134. MeltSagaState::SetupComplete => "setup_complete",
  135. MeltSagaState::PaymentSent => "payment_sent",
  136. },
  137. }
  138. }
  139. }
  140. /// Persisted saga for recovery
  141. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
  142. pub struct Saga {
  143. /// Operation ID (correlation key)
  144. pub operation_id: Uuid,
  145. /// Operation kind (swap, mint, melt)
  146. pub operation_kind: OperationKind,
  147. /// Current saga state (operation-specific)
  148. pub state: SagaStateEnum,
  149. /// Blinded secrets (B values) from output blinded messages
  150. pub blinded_secrets: Vec<PublicKey>,
  151. /// Y values (public keys) from input proofs
  152. pub input_ys: Vec<PublicKey>,
  153. /// Quote ID for melt operations (used for payment status lookup during recovery)
  154. /// None for swap operations
  155. pub quote_id: Option<String>,
  156. /// Unix timestamp when saga was created
  157. pub created_at: u64,
  158. /// Unix timestamp when saga was last updated
  159. pub updated_at: u64,
  160. }
  161. impl Saga {
  162. /// Create new swap saga
  163. pub fn new_swap(
  164. operation_id: Uuid,
  165. state: SwapSagaState,
  166. blinded_secrets: Vec<PublicKey>,
  167. input_ys: Vec<PublicKey>,
  168. ) -> Self {
  169. let now = unix_time();
  170. Self {
  171. operation_id,
  172. operation_kind: OperationKind::Swap,
  173. state: SagaStateEnum::Swap(state),
  174. blinded_secrets,
  175. input_ys,
  176. quote_id: None,
  177. created_at: now,
  178. updated_at: now,
  179. }
  180. }
  181. /// Update swap saga state
  182. pub fn update_swap_state(&mut self, new_state: SwapSagaState) {
  183. self.state = SagaStateEnum::Swap(new_state);
  184. self.updated_at = unix_time();
  185. }
  186. /// Create new melt saga
  187. pub fn new_melt(
  188. operation_id: Uuid,
  189. state: MeltSagaState,
  190. input_ys: Vec<PublicKey>,
  191. blinded_secrets: Vec<PublicKey>,
  192. quote_id: String,
  193. ) -> Self {
  194. let now = unix_time();
  195. Self {
  196. operation_id,
  197. operation_kind: OperationKind::Melt,
  198. state: SagaStateEnum::Melt(state),
  199. blinded_secrets,
  200. input_ys,
  201. quote_id: Some(quote_id),
  202. created_at: now,
  203. updated_at: now,
  204. }
  205. }
  206. /// Update melt saga state
  207. pub fn update_melt_state(&mut self, new_state: MeltSagaState) {
  208. self.state = SagaStateEnum::Melt(new_state);
  209. self.updated_at = unix_time();
  210. }
  211. }
  212. /// Operation
  213. pub enum Operation {
  214. /// Mint
  215. Mint(Uuid),
  216. /// Melt
  217. Melt(Uuid),
  218. /// Swap
  219. Swap(Uuid),
  220. }
  221. impl Operation {
  222. /// Mint
  223. pub fn new_mint() -> Self {
  224. Self::Mint(Uuid::new_v4())
  225. }
  226. /// Melt
  227. pub fn new_melt() -> Self {
  228. Self::Melt(Uuid::new_v4())
  229. }
  230. /// Swap
  231. pub fn new_swap() -> Self {
  232. Self::Swap(Uuid::new_v4())
  233. }
  234. /// Operation id
  235. pub fn id(&self) -> &Uuid {
  236. match self {
  237. Operation::Mint(id) => id,
  238. Operation::Melt(id) => id,
  239. Operation::Swap(id) => id,
  240. }
  241. }
  242. /// Operation kind
  243. pub fn kind(&self) -> &str {
  244. match self {
  245. Operation::Mint(_) => "mint",
  246. Operation::Melt(_) => "melt",
  247. Operation::Swap(_) => "swap",
  248. }
  249. }
  250. /// From kind and i
  251. pub fn from_kind_and_id(kind: &str, id: &str) -> Result<Self, Error> {
  252. let uuid = Uuid::parse_str(id)?;
  253. match kind {
  254. "mint" => Ok(Self::Mint(uuid)),
  255. "melt" => Ok(Self::Melt(uuid)),
  256. "swap" => Ok(Self::Swap(uuid)),
  257. _ => Err(Error::Custom(format!("Invalid operation kind: {kind}"))),
  258. }
  259. }
  260. }
  261. /// Mint Quote Info
  262. #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
  263. pub struct MintQuote {
  264. /// Quote id
  265. pub id: QuoteId,
  266. /// Amount of quote
  267. pub amount: Option<Amount>,
  268. /// Unit of quote
  269. pub unit: CurrencyUnit,
  270. /// Quote payment request e.g. bolt11
  271. pub request: String,
  272. /// Expiration time of quote
  273. pub expiry: u64,
  274. /// Value used by ln backend to look up state of request
  275. pub request_lookup_id: PaymentIdentifier,
  276. /// Pubkey
  277. pub pubkey: Option<PublicKey>,
  278. /// Unix time quote was created
  279. #[serde(default)]
  280. pub created_time: u64,
  281. /// Amount paid
  282. #[serde(default)]
  283. amount_paid: Amount,
  284. /// Amount issued
  285. #[serde(default)]
  286. amount_issued: Amount,
  287. /// Payment of payment(s) that filled quote
  288. #[serde(default)]
  289. pub payments: Vec<IncomingPayment>,
  290. /// Payment Method
  291. #[serde(default)]
  292. pub payment_method: PaymentMethod,
  293. /// Payment of payment(s) that filled quote
  294. #[serde(default)]
  295. pub issuance: Vec<Issuance>,
  296. }
  297. impl MintQuote {
  298. /// Create new [`MintQuote`]
  299. #[allow(clippy::too_many_arguments)]
  300. pub fn new(
  301. id: Option<QuoteId>,
  302. request: String,
  303. unit: CurrencyUnit,
  304. amount: Option<Amount>,
  305. expiry: u64,
  306. request_lookup_id: PaymentIdentifier,
  307. pubkey: Option<PublicKey>,
  308. amount_paid: Amount,
  309. amount_issued: Amount,
  310. payment_method: PaymentMethod,
  311. created_time: u64,
  312. payments: Vec<IncomingPayment>,
  313. issuance: Vec<Issuance>,
  314. ) -> Self {
  315. let id = id.unwrap_or_else(QuoteId::new_uuid);
  316. Self {
  317. id,
  318. amount,
  319. unit,
  320. request,
  321. expiry,
  322. request_lookup_id,
  323. pubkey,
  324. created_time,
  325. amount_paid,
  326. amount_issued,
  327. payment_method,
  328. payments,
  329. issuance,
  330. }
  331. }
  332. /// Increment the amount paid on the mint quote by a given amount
  333. #[instrument(skip(self))]
  334. pub fn increment_amount_paid(
  335. &mut self,
  336. additional_amount: Amount,
  337. ) -> Result<Amount, crate::Error> {
  338. self.amount_paid = self
  339. .amount_paid
  340. .checked_add(additional_amount)
  341. .ok_or(crate::Error::AmountOverflow)?;
  342. Ok(self.amount_paid)
  343. }
  344. /// Amount paid
  345. #[instrument(skip(self))]
  346. pub fn amount_paid(&self) -> Amount {
  347. self.amount_paid
  348. }
  349. /// Increment the amount issued on the mint quote by a given amount
  350. #[instrument(skip(self))]
  351. pub fn increment_amount_issued(
  352. &mut self,
  353. additional_amount: Amount,
  354. ) -> Result<Amount, crate::Error> {
  355. self.amount_issued = self
  356. .amount_issued
  357. .checked_add(additional_amount)
  358. .ok_or(crate::Error::AmountOverflow)?;
  359. Ok(self.amount_issued)
  360. }
  361. /// Amount issued
  362. #[instrument(skip(self))]
  363. pub fn amount_issued(&self) -> Amount {
  364. self.amount_issued
  365. }
  366. /// Get state of mint quote
  367. #[instrument(skip(self))]
  368. pub fn state(&self) -> MintQuoteState {
  369. self.compute_quote_state()
  370. }
  371. /// Existing payment ids of a mint quote
  372. pub fn payment_ids(&self) -> Vec<&String> {
  373. self.payments.iter().map(|a| &a.payment_id).collect()
  374. }
  375. /// Amount mintable
  376. /// Returns the amount that is still available for minting.
  377. ///
  378. /// The value is computed as the difference between the total amount that
  379. /// has been paid for this issuance (`self.amount_paid`) and the amount
  380. /// that has already been issued (`self.amount_issued`). In other words,
  381. pub fn amount_mintable(&self) -> Amount {
  382. self.amount_paid - self.amount_issued
  383. }
  384. /// Add a payment ID to the list of payment IDs
  385. ///
  386. /// Returns an error if the payment ID is already in the list
  387. #[instrument(skip(self))]
  388. pub fn add_payment(
  389. &mut self,
  390. amount: Amount,
  391. payment_id: String,
  392. time: u64,
  393. ) -> Result<(), crate::Error> {
  394. let payment_ids = self.payment_ids();
  395. if payment_ids.contains(&&payment_id) {
  396. return Err(crate::Error::DuplicatePaymentId);
  397. }
  398. let payment = IncomingPayment::new(amount, payment_id, time);
  399. self.payments.push(payment);
  400. Ok(())
  401. }
  402. /// Compute quote state
  403. #[instrument(skip(self))]
  404. fn compute_quote_state(&self) -> MintQuoteState {
  405. if self.amount_paid == Amount::ZERO && self.amount_issued == Amount::ZERO {
  406. return MintQuoteState::Unpaid;
  407. }
  408. match self.amount_paid.cmp(&self.amount_issued) {
  409. std::cmp::Ordering::Less => {
  410. // self.amount_paid is less than other (amount issued)
  411. // Handle case where paid amount is insufficient
  412. tracing::error!("We should not have issued more then has been paid");
  413. MintQuoteState::Issued
  414. }
  415. std::cmp::Ordering::Equal => {
  416. // We do this extra check for backwards compatibility for quotes where amount paid/issed was not tracked
  417. // self.amount_paid equals other (amount issued)
  418. // Handle case where paid amount exactly matches
  419. MintQuoteState::Issued
  420. }
  421. std::cmp::Ordering::Greater => {
  422. // self.amount_paid is greater than other (amount issued)
  423. // Handle case where paid amount exceeds required amount
  424. MintQuoteState::Paid
  425. }
  426. }
  427. }
  428. }
  429. /// Mint Payments
  430. #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
  431. pub struct IncomingPayment {
  432. /// Amount
  433. pub amount: Amount,
  434. /// Pyament unix time
  435. pub time: u64,
  436. /// Payment id
  437. pub payment_id: String,
  438. }
  439. impl IncomingPayment {
  440. /// New [`IncomingPayment`]
  441. pub fn new(amount: Amount, payment_id: String, time: u64) -> Self {
  442. Self {
  443. payment_id,
  444. time,
  445. amount,
  446. }
  447. }
  448. }
  449. /// Informattion about issued quote
  450. #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
  451. pub struct Issuance {
  452. /// Amount
  453. pub amount: Amount,
  454. /// Time
  455. pub time: u64,
  456. }
  457. impl Issuance {
  458. /// Create new [`Issuance`]
  459. pub fn new(amount: Amount, time: u64) -> Self {
  460. Self { amount, time }
  461. }
  462. }
  463. /// Melt Quote Info
  464. #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
  465. pub struct MeltQuote {
  466. /// Quote id
  467. pub id: QuoteId,
  468. /// Quote unit
  469. pub unit: CurrencyUnit,
  470. /// Quote amount
  471. pub amount: Amount,
  472. /// Quote Payment request e.g. bolt11
  473. pub request: MeltPaymentRequest,
  474. /// Quote fee reserve
  475. pub fee_reserve: Amount,
  476. /// Quote state
  477. pub state: MeltQuoteState,
  478. /// Expiration time of quote
  479. pub expiry: u64,
  480. /// Payment preimage
  481. pub payment_preimage: Option<String>,
  482. /// Value used by ln backend to look up state of request
  483. pub request_lookup_id: Option<PaymentIdentifier>,
  484. /// Payment options
  485. ///
  486. /// Used for amountless invoices and MPP payments
  487. pub options: Option<MeltOptions>,
  488. /// Unix time quote was created
  489. #[serde(default)]
  490. pub created_time: u64,
  491. /// Unix time quote was paid
  492. pub paid_time: Option<u64>,
  493. /// Payment method
  494. #[serde(default)]
  495. pub payment_method: PaymentMethod,
  496. }
  497. impl MeltQuote {
  498. /// Create new [`MeltQuote`]
  499. #[allow(clippy::too_many_arguments)]
  500. pub fn new(
  501. request: MeltPaymentRequest,
  502. unit: CurrencyUnit,
  503. amount: Amount,
  504. fee_reserve: Amount,
  505. expiry: u64,
  506. request_lookup_id: Option<PaymentIdentifier>,
  507. options: Option<MeltOptions>,
  508. payment_method: PaymentMethod,
  509. ) -> Self {
  510. let id = Uuid::new_v4();
  511. Self {
  512. id: QuoteId::UUID(id),
  513. amount,
  514. unit,
  515. request,
  516. fee_reserve,
  517. state: MeltQuoteState::Unpaid,
  518. expiry,
  519. payment_preimage: None,
  520. request_lookup_id,
  521. options,
  522. created_time: unix_time(),
  523. paid_time: None,
  524. payment_method,
  525. }
  526. }
  527. }
  528. /// Mint Keyset Info
  529. #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
  530. pub struct MintKeySetInfo {
  531. /// Keyset [`Id`]
  532. pub id: Id,
  533. /// Keyset [`CurrencyUnit`]
  534. pub unit: CurrencyUnit,
  535. /// Keyset active or inactive
  536. /// Mint will only issue new signatures on active keysets
  537. pub active: bool,
  538. /// Starting unix time Keyset is valid from
  539. pub valid_from: u64,
  540. /// [`DerivationPath`] keyset
  541. pub derivation_path: DerivationPath,
  542. /// DerivationPath index of Keyset
  543. pub derivation_path_index: Option<u32>,
  544. /// Max order of keyset
  545. pub max_order: u8,
  546. /// Supported amounts
  547. pub amounts: Vec<u64>,
  548. /// Input Fee ppk
  549. #[serde(default = "default_fee")]
  550. pub input_fee_ppk: u64,
  551. /// Final expiry
  552. pub final_expiry: Option<u64>,
  553. }
  554. /// Default fee
  555. pub fn default_fee() -> u64 {
  556. 0
  557. }
  558. impl From<MintKeySetInfo> for KeySetInfo {
  559. fn from(keyset_info: MintKeySetInfo) -> Self {
  560. Self {
  561. id: keyset_info.id,
  562. unit: keyset_info.unit,
  563. active: keyset_info.active,
  564. input_fee_ppk: keyset_info.input_fee_ppk,
  565. final_expiry: keyset_info.final_expiry,
  566. }
  567. }
  568. }
  569. impl From<MintQuote> for MintQuoteBolt11Response<QuoteId> {
  570. fn from(mint_quote: crate::mint::MintQuote) -> MintQuoteBolt11Response<QuoteId> {
  571. MintQuoteBolt11Response {
  572. quote: mint_quote.id.clone(),
  573. state: mint_quote.state(),
  574. request: mint_quote.request,
  575. expiry: Some(mint_quote.expiry),
  576. pubkey: mint_quote.pubkey,
  577. amount: mint_quote.amount,
  578. unit: Some(mint_quote.unit.clone()),
  579. }
  580. }
  581. }
  582. impl From<MintQuote> for MintQuoteBolt11Response<String> {
  583. fn from(quote: MintQuote) -> Self {
  584. let quote: MintQuoteBolt11Response<QuoteId> = quote.into();
  585. quote.into()
  586. }
  587. }
  588. impl TryFrom<crate::mint::MintQuote> for MintQuoteBolt12Response<QuoteId> {
  589. type Error = crate::Error;
  590. fn try_from(mint_quote: crate::mint::MintQuote) -> Result<Self, Self::Error> {
  591. Ok(MintQuoteBolt12Response {
  592. quote: mint_quote.id.clone(),
  593. request: mint_quote.request,
  594. expiry: Some(mint_quote.expiry),
  595. amount_paid: mint_quote.amount_paid,
  596. amount_issued: mint_quote.amount_issued,
  597. pubkey: mint_quote.pubkey.ok_or(crate::Error::PubkeyRequired)?,
  598. amount: mint_quote.amount,
  599. unit: mint_quote.unit,
  600. })
  601. }
  602. }
  603. impl TryFrom<MintQuote> for MintQuoteBolt12Response<String> {
  604. type Error = crate::Error;
  605. fn try_from(quote: MintQuote) -> Result<Self, Self::Error> {
  606. let quote: MintQuoteBolt12Response<QuoteId> = quote.try_into()?;
  607. Ok(quote.into())
  608. }
  609. }
  610. impl From<&MeltQuote> for MeltQuoteBolt11Response<QuoteId> {
  611. fn from(melt_quote: &MeltQuote) -> MeltQuoteBolt11Response<QuoteId> {
  612. MeltQuoteBolt11Response {
  613. quote: melt_quote.id.clone(),
  614. payment_preimage: None,
  615. change: None,
  616. state: melt_quote.state,
  617. paid: Some(melt_quote.state == MeltQuoteState::Paid),
  618. expiry: melt_quote.expiry,
  619. amount: melt_quote.amount,
  620. fee_reserve: melt_quote.fee_reserve,
  621. request: None,
  622. unit: Some(melt_quote.unit.clone()),
  623. }
  624. }
  625. }
  626. impl From<MeltQuote> for MeltQuoteBolt11Response<QuoteId> {
  627. fn from(melt_quote: MeltQuote) -> MeltQuoteBolt11Response<QuoteId> {
  628. let paid = melt_quote.state == MeltQuoteState::Paid;
  629. MeltQuoteBolt11Response {
  630. quote: melt_quote.id.clone(),
  631. amount: melt_quote.amount,
  632. fee_reserve: melt_quote.fee_reserve,
  633. paid: Some(paid),
  634. state: melt_quote.state,
  635. expiry: melt_quote.expiry,
  636. payment_preimage: melt_quote.payment_preimage,
  637. change: None,
  638. request: Some(melt_quote.request.to_string()),
  639. unit: Some(melt_quote.unit.clone()),
  640. }
  641. }
  642. }
  643. /// Payment request
  644. #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
  645. pub enum MeltPaymentRequest {
  646. /// Bolt11 Payment
  647. Bolt11 {
  648. /// Bolt11 invoice
  649. bolt11: Bolt11Invoice,
  650. },
  651. /// Bolt12 Payment
  652. Bolt12 {
  653. /// Offer
  654. #[serde(with = "offer_serde")]
  655. offer: Box<Offer>,
  656. },
  657. }
  658. impl std::fmt::Display for MeltPaymentRequest {
  659. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  660. match self {
  661. MeltPaymentRequest::Bolt11 { bolt11 } => write!(f, "{bolt11}"),
  662. MeltPaymentRequest::Bolt12 { offer } => write!(f, "{offer}"),
  663. }
  664. }
  665. }
  666. mod offer_serde {
  667. use std::str::FromStr;
  668. use serde::{self, Deserialize, Deserializer, Serializer};
  669. use super::Offer;
  670. pub fn serialize<S>(offer: &Offer, serializer: S) -> Result<S::Ok, S::Error>
  671. where
  672. S: Serializer,
  673. {
  674. let s = offer.to_string();
  675. serializer.serialize_str(&s)
  676. }
  677. pub fn deserialize<'de, D>(deserializer: D) -> Result<Box<Offer>, D::Error>
  678. where
  679. D: Deserializer<'de>,
  680. {
  681. let s = String::deserialize(deserializer)?;
  682. Ok(Box::new(Offer::from_str(&s).map_err(|_| {
  683. serde::de::Error::custom("Invalid Bolt12 Offer")
  684. })?))
  685. }
  686. }