use serde::{Deserialize, Serialize};
use super::amount::{Amount, CurrencyUnit};
use super::mint::MintUrl;
use crate::error::FfiError;
#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
pub struct MintQuote {
pub id: String,
pub amount: Option<Amount>,
pub unit: CurrencyUnit,
pub request: String,
pub state: QuoteState,
pub expiry: u64,
pub mint_url: MintUrl,
pub amount_issued: Amount,
pub amount_paid: Amount,
pub estimated_blocks: Option<u32>,
pub payment_method: PaymentMethod,
pub secret_key: Option<String>,
pub used_by_operation: Option<String>,
#[serde(default)]
pub version: u32,
}
impl From<cdk::wallet::MintQuote> for MintQuote {
fn from(quote: cdk::wallet::MintQuote) -> Self {
Self {
id: quote.id.clone(),
amount: quote.amount.map(Into::into),
unit: quote.unit.clone().into(),
request: quote.request.clone(),
state: quote.state.into(),
expiry: quote.expiry,
mint_url: quote.mint_url.clone().into(),
amount_issued: quote.amount_issued.into(),
amount_paid: quote.amount_paid.into(),
estimated_blocks: quote.estimated_blocks,
payment_method: quote.payment_method.into(),
secret_key: quote.secret_key.map(|sk| sk.to_secret_hex()),
used_by_operation: quote.used_by_operation.map(|id| id.to_string()),
version: quote.version,
}
}
}
impl TryFrom<MintQuote> for cdk::wallet::MintQuote {
type Error = FfiError;
fn try_from(quote: MintQuote) -> Result<Self, Self::Error> {
let secret_key = quote
.secret_key
.map(|hex| cdk::nuts::SecretKey::from_hex(&hex))
.transpose()
.map_err(|e| FfiError::internal(format!("Invalid secret key: {}", e)))?;
Ok(Self {
id: quote.id,
amount: quote.amount.map(Into::into),
unit: quote.unit.into(),
request: quote.request,
state: quote.state.into(),
expiry: quote.expiry,
mint_url: quote.mint_url.try_into()?,
amount_issued: quote.amount_issued.into(),
amount_paid: quote.amount_paid.into(),
estimated_blocks: quote.estimated_blocks,
payment_method: quote.payment_method.into(),
secret_key,
used_by_operation: quote.used_by_operation,
version: quote.version,
})
}
}
#[uniffi::export]
pub fn mint_quote_total_amount(quote: &MintQuote) -> Result<Amount, FfiError> {
let cdk_quote: cdk::wallet::MintQuote = quote.clone().try_into()?;
Ok(cdk_quote.total_amount().into())
}
#[uniffi::export]
pub fn mint_quote_is_expired(quote: &MintQuote, current_time: u64) -> Result<bool, FfiError> {
let cdk_quote: cdk::wallet::MintQuote = quote.clone().try_into()?;
Ok(cdk_quote.is_expired(current_time))
}
#[uniffi::export]
pub fn mint_quote_amount_mintable(quote: &MintQuote) -> Result<Amount, FfiError> {
let cdk_quote: cdk::wallet::MintQuote = quote.clone().try_into()?;
Ok(cdk_quote.amount_mintable().into())
}
#[uniffi::export]
pub fn decode_mint_quote(json: String) -> Result<MintQuote, FfiError> {
let quote: cdk::wallet::MintQuote = serde_json::from_str(&json)?;
Ok(quote.into())
}
#[uniffi::export]
pub fn encode_mint_quote(quote: MintQuote) -> Result<String, FfiError> {
Ok(serde_json::to_string("e)?)
}
#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
pub struct MintQuoteBolt11Response {
pub quote: String,
pub request: String,
pub state: QuoteState,
pub expiry: Option<u64>,
pub amount: Option<Amount>,
pub unit: Option<CurrencyUnit>,
pub pubkey: Option<String>,
}
impl From<cdk::nuts::MintQuoteBolt11Response<String>> for MintQuoteBolt11Response {
fn from(response: cdk::nuts::MintQuoteBolt11Response<String>) -> Self {
Self {
quote: response.quote,
request: response.request,
state: response.state.into(),
expiry: response.expiry,
amount: response.amount.map(Into::into),
unit: response.unit.map(Into::into),
pubkey: response.pubkey.map(|p| p.to_string()),
}
}
}
impl From<cdk::wallet::MintQuote> for MintQuoteBolt11Response {
fn from(quote: cdk::wallet::MintQuote) -> Self {
Self {
quote: quote.id,
request: quote.request,
state: quote.state.into(),
expiry: Some(quote.expiry),
amount: quote.amount.map(Into::into),
unit: Some(quote.unit.into()),
pubkey: quote.secret_key.map(|sk| sk.public_key().to_string()),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
pub struct MintQuoteCustomResponse {
pub quote: String,
pub request: String,
pub expiry: Option<u64>,
pub amount: Option<Amount>,
pub amount_paid: Amount,
pub amount_issued: Amount,
pub unit: Option<CurrencyUnit>,
pub pubkey: Option<String>,
pub extra: Option<String>,
}
impl From<cdk::nuts::MintQuoteCustomResponse<String>> for MintQuoteCustomResponse {
fn from(response: cdk::nuts::MintQuoteCustomResponse<String>) -> Self {
let extra = if response.extra.is_null() {
None
} else {
Some(response.extra.to_string())
};
Self {
quote: response.quote,
request: response.request,
expiry: response.expiry,
amount: response.amount.map(Into::into),
amount_paid: response.amount_paid.into(),
amount_issued: response.amount_issued.into(),
unit: response.unit.map(Into::into),
pubkey: response.pubkey.map(|p| p.to_string()),
extra,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
pub struct MeltQuoteBolt11Response {
pub quote: String,
pub amount: Amount,
pub fee_reserve: Amount,
pub state: QuoteState,
pub expiry: u64,
pub payment_proof: Option<String>,
pub request: Option<String>,
pub unit: Option<CurrencyUnit>,
}
impl From<cdk::nuts::MeltQuoteBolt11Response<String>> for MeltQuoteBolt11Response {
fn from(response: cdk::nuts::MeltQuoteBolt11Response<String>) -> Self {
Self {
quote: response.quote,
amount: response.amount.into(),
fee_reserve: response.fee_reserve.into(),
state: response.state.into(),
expiry: response.expiry,
payment_proof: response.payment_preimage,
request: response.request,
unit: response.unit.map(Into::into),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
pub struct MeltQuoteCustomResponse {
pub quote: String,
pub amount: Amount,
pub fee_reserve: Option<Amount>,
pub state: QuoteState,
pub expiry: u64,
pub payment_proof: Option<String>,
pub request: Option<String>,
pub unit: Option<CurrencyUnit>,
pub extra: Option<String>,
}
impl From<cdk::nuts::MeltQuoteCustomResponse<String>> for MeltQuoteCustomResponse {
fn from(response: cdk::nuts::MeltQuoteCustomResponse<String>) -> Self {
let extra = if response.extra.is_null() {
None
} else {
Some(response.extra.to_string())
};
Self {
quote: response.quote,
amount: response.amount.into(),
fee_reserve: response.fee_reserve.map(Into::into),
state: response.state.into(),
expiry: response.expiry,
payment_proof: response.payment_preimage,
request: response.request,
unit: response.unit.map(Into::into),
extra,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, uniffi::Enum)]
pub enum PaymentMethod {
Bolt11,
Bolt12,
Onchain,
Custom { method: String },
}
impl From<cdk::nuts::PaymentMethod> for PaymentMethod {
fn from(method: cdk::nuts::PaymentMethod) -> Self {
match method.as_str() {
"bolt11" => Self::Bolt11,
"bolt12" => Self::Bolt12,
"onchain" => Self::Onchain,
s => Self::Custom {
method: s.to_string(),
},
}
}
}
impl From<PaymentMethod> for cdk::nuts::PaymentMethod {
fn from(method: PaymentMethod) -> Self {
match method {
PaymentMethod::Bolt11 => Self::from("bolt11"),
PaymentMethod::Bolt12 => Self::from("bolt12"),
PaymentMethod::Onchain => Self::from("onchain"),
PaymentMethod::Custom { method } => Self::from(method),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
pub struct MintQuoteOnchainResponse {
pub quote: String,
pub request: String,
pub unit: CurrencyUnit,
pub expiry: Option<u64>,
pub pubkey: String,
pub amount_paid: Amount,
pub amount_issued: Amount,
}
impl From<cdk::nuts::MintQuoteOnchainResponse<String>> for MintQuoteOnchainResponse {
fn from(response: cdk::nuts::MintQuoteOnchainResponse<String>) -> Self {
Self {
quote: response.quote,
request: response.request,
unit: response.unit.into(),
expiry: response.expiry,
pubkey: response.pubkey.to_string(),
amount_paid: response.amount_paid.into(),
amount_issued: response.amount_issued.into(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
pub struct MeltQuoteOnchainFeeOption {
pub fee_index: u32,
pub fee_reserve: Amount,
pub estimated_blocks: u32,
}
impl From<cdk::nuts::nut30::MeltQuoteOnchainFeeOption> for MeltQuoteOnchainFeeOption {
fn from(option: cdk::nuts::nut30::MeltQuoteOnchainFeeOption) -> Self {
Self {
fee_index: option.fee_index,
fee_reserve: option.fee_reserve.into(),
estimated_blocks: option.estimated_blocks,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
pub struct MeltQuoteOnchainResponse {
pub quote: String,
pub amount: Amount,
pub unit: CurrencyUnit,
pub state: QuoteState,
pub expiry: u64,
pub request: String,
pub fee_options: Vec<MeltQuoteOnchainFeeOption>,
pub selected_fee_index: Option<u32>,
pub outpoint: Option<String>,
pub change: Option<String>,
}
impl From<cdk::nuts::MeltQuoteOnchainResponse<String>> for MeltQuoteOnchainResponse {
fn from(response: cdk::nuts::MeltQuoteOnchainResponse<String>) -> Self {
let change = response
.change
.as_ref()
.and_then(|change| serde_json::to_string(change).ok());
Self {
quote: response.quote,
amount: response.amount.into(),
unit: response.unit.into(),
state: response.state.into(),
expiry: response.expiry,
request: response.request,
fee_options: response.fee_options.into_iter().map(Into::into).collect(),
selected_fee_index: response.selected_fee_index,
outpoint: response.outpoint,
change,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
pub struct MeltQuote {
pub id: String,
pub mint_url: Option<MintUrl>,
pub amount: Amount,
pub unit: CurrencyUnit,
pub request: String,
pub fee_reserve: Amount,
pub state: QuoteState,
pub expiry: u64,
pub payment_proof: Option<String>,
pub estimated_blocks: Option<u32>,
pub fee_index: Option<u32>,
pub payment_method: PaymentMethod,
pub used_by_operation: Option<String>,
#[serde(default)]
pub version: u32,
}
impl From<cdk::wallet::MeltQuote> for MeltQuote {
fn from(quote: cdk::wallet::MeltQuote) -> Self {
Self {
id: quote.id.clone(),
mint_url: quote.mint_url.map(Into::into),
amount: quote.amount.into(),
unit: quote.unit.clone().into(),
request: quote.request.clone(),
fee_reserve: quote.fee_reserve.into(),
state: quote.state.into(),
expiry: quote.expiry,
payment_proof: quote.payment_proof.clone(),
estimated_blocks: quote.estimated_blocks,
fee_index: quote.fee_index,
payment_method: quote.payment_method.into(),
used_by_operation: quote.used_by_operation.map(|id| id.to_string()),
version: quote.version,
}
}
}
impl TryFrom<MeltQuote> for cdk::wallet::MeltQuote {
type Error = FfiError;
fn try_from(quote: MeltQuote) -> Result<Self, Self::Error> {
Ok(Self {
id: quote.id,
mint_url: quote.mint_url.map(|m| m.try_into()).transpose()?,
amount: quote.amount.into(),
unit: quote.unit.into(),
request: quote.request,
fee_reserve: quote.fee_reserve.into(),
state: quote.state.into(),
expiry: quote.expiry,
payment_proof: quote.payment_proof,
estimated_blocks: quote.estimated_blocks,
fee_index: quote.fee_index,
payment_method: quote.payment_method.into(),
used_by_operation: quote.used_by_operation,
version: quote.version,
})
}
}
impl MeltQuote {
pub fn to_json(&self) -> Result<String, FfiError> {
Ok(serde_json::to_string(self)?)
}
}
#[uniffi::export]
pub fn decode_melt_quote(json: String) -> Result<MeltQuote, FfiError> {
let quote: cdk::wallet::MeltQuote = serde_json::from_str(&json)?;
Ok(quote.into())
}
#[uniffi::export]
pub fn encode_melt_quote(quote: MeltQuote) -> Result<String, FfiError> {
Ok(serde_json::to_string("e)?)
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, uniffi::Enum)]
pub enum QuoteState {
Unpaid,
Paid,
Pending,
Issued,
}
impl From<cdk::nuts::nut05::QuoteState> for QuoteState {
fn from(state: cdk::nuts::nut05::QuoteState) -> Self {
match state {
cdk::nuts::nut05::QuoteState::Unpaid => QuoteState::Unpaid,
cdk::nuts::nut05::QuoteState::Paid => QuoteState::Paid,
cdk::nuts::nut05::QuoteState::Pending => QuoteState::Pending,
cdk::nuts::nut05::QuoteState::Unknown => QuoteState::Unpaid,
cdk::nuts::nut05::QuoteState::Failed => QuoteState::Unpaid,
}
}
}
impl From<QuoteState> for cdk::nuts::nut05::QuoteState {
fn from(state: QuoteState) -> Self {
match state {
QuoteState::Unpaid => cdk::nuts::nut05::QuoteState::Unpaid,
QuoteState::Paid => cdk::nuts::nut05::QuoteState::Paid,
QuoteState::Pending => cdk::nuts::nut05::QuoteState::Pending,
QuoteState::Issued => cdk::nuts::nut05::QuoteState::Paid, }
}
}
impl From<cdk::nuts::MintQuoteState> for QuoteState {
fn from(state: cdk::nuts::MintQuoteState) -> Self {
match state {
cdk::nuts::MintQuoteState::Unpaid => QuoteState::Unpaid,
cdk::nuts::MintQuoteState::Paid => QuoteState::Paid,
cdk::nuts::MintQuoteState::Issued => QuoteState::Issued,
}
}
}
impl From<QuoteState> for cdk::nuts::MintQuoteState {
fn from(state: QuoteState) -> Self {
match state {
QuoteState::Unpaid => cdk::nuts::MintQuoteState::Unpaid,
QuoteState::Paid => cdk::nuts::MintQuoteState::Paid,
QuoteState::Issued => cdk::nuts::MintQuoteState::Issued,
QuoteState::Pending => cdk::nuts::MintQuoteState::Unpaid,
}
}
}