From 652c23800e049b796580898a93593e9f5bb2741d Mon Sep 17 00:00:00 2001 From: Ross Savage <551697+dangeross@users.noreply.github.com> Date: Fri, 24 Jan 2025 16:57:22 +0100 Subject: [PATCH] Configure asset metadata (#659) * Configure asset metadata * Apply suggestions from code review * Return BIP21 URI also when only non-bitcoin asset_id is defined * Rename AssetMetadata functions --- cli/Cargo.lock | 2 +- cli/src/commands.rs | 70 +-- lib/Cargo.lock | 2 +- .../include/breez_sdk_liquid.h | 41 +- lib/bindings/src/breez_sdk_liquid.udl | 27 +- lib/core/Cargo.toml | 2 +- lib/core/src/bindings.rs | 1 + lib/core/src/error.rs | 9 + lib/core/src/frb_generated.rs | 472 +++++++++++++++--- lib/core/src/model.rs | 66 ++- lib/core/src/persist/asset_metadata.rs | 64 +++ lib/core/src/persist/migrations.rs | 26 + lib/core/src/persist/mod.rs | 58 ++- lib/core/src/sdk.rs | 154 ++++-- packages/dart/lib/src/bindings.dart | 4 + packages/dart/lib/src/error.dart | 3 + packages/dart/lib/src/error.freezed.dart | 79 +++ packages/dart/lib/src/frb_generated.dart | 329 +++++++++--- packages/dart/lib/src/frb_generated.io.dart | 233 ++++++++- packages/dart/lib/src/model.dart | 108 +++- packages/dart/lib/src/model.freezed.dart | 46 +- ...utter_breez_liquid_bindings_generated.dart | 79 ++- .../breezsdkliquid/BreezSDKLiquidMapper.kt | 121 ++++- .../ios/BreezSDKLiquidMapper.swift | 145 +++++- packages/react-native/src/index.ts | 21 +- 25 files changed, 1851 insertions(+), 311 deletions(-) create mode 100644 lib/core/src/persist/asset_metadata.rs diff --git a/cli/Cargo.lock b/cli/Cargo.lock index 16020dbb2..e8bdff8e8 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -3941,7 +3941,7 @@ dependencies = [ [[package]] name = "sdk-common" version = "0.6.2" -source = "git+https://github.com/breez/breez-sdk?rev=db5e6dbac3568ebd62b44a879ce5dab30b02d394#db5e6dbac3568ebd62b44a879ce5dab30b02d394" +source = "git+https://github.com/breez/breez-sdk?rev=5a20b38b4c3b574282e8d6e5a61750bb7f4bebed#5a20b38b4c3b574282e8d6e5a61750bb7f4bebed" dependencies = [ "aes 0.8.4", "anyhow", diff --git a/cli/src/commands.rs b/cli/src/commands.rs index 9b91b0cf1..529eec1fe 100644 --- a/cli/src/commands.rs +++ b/cli/src/commands.rs @@ -22,28 +22,33 @@ pub(crate) enum Command { /// Send a payment directly or via a swap SendPayment { /// Invoice which has to be paid (BOLT11) - #[arg(long)] + #[arg(short, long)] invoice: Option, /// BOLT12 offer. If specified, amount_sat must also be set. - #[arg(long)] + #[arg(short, long)] offer: Option, /// Either BIP21 URI or Liquid address we intend to pay to - #[arg(long)] + #[arg(short, long)] address: Option, - /// The amount to pay, in case of a direct Liquid address or amount-less BIP21. - /// If an asset id is provided, it is the base unit of that asset depending on its - /// precision, otherwise in satoshi. - #[arg(short, long)] - receiver_amount: Option, + /// The amount to pay, in satoshi. The amount is optional if it is already provided in the + /// invoice or BIP21 URI. + #[arg(long)] + amount_sat: Option, /// Optional id of the asset, in case of a direct Liquid address /// or amount-less BIP21 #[clap(long = "asset")] asset_id: Option, + /// The amount to pay, in case of a Liquid payment. The amount is optional if it is already + /// provided in the BIP21 URI. + /// The asset id must also be provided. + #[arg(long)] + amount: Option, + /// Whether or not this is a drain operation. If true, all available funds will be used. #[arg(short, long)] drain: Option, @@ -78,16 +83,6 @@ pub(crate) enum Command { #[arg(short = 'm', long = "method")] payment_method: Option, - /// The amount the payer should send. If an asset id is provided, it is the base - /// unit of that asset depending on its precision, otherwise in satoshi. - /// If not specified, it will generate a BIP21 URI/address with no amount. - #[arg(short, long)] - payer_amount: Option, - - /// Optional id of the asset to receive when the 'payment_method' is "liquid" - #[clap(long = "asset")] - asset_id: Option, - /// Optional description for the invoice #[clap(short = 'd', long = "description")] description: Option, @@ -95,6 +90,21 @@ pub(crate) enum Command { /// Optional if true uses the hash of the description #[clap(name = "use_description_hash", short = 's', long = "desc_hash")] use_description_hash: Option, + + /// The amount the payer should send, in satoshi. If not specified, it will generate a + /// BIP21 URI/address with no amount. + #[arg(long)] + amount_sat: Option, + + /// Optional id of the asset to receive when the 'payment_method' is "liquid" + #[clap(long = "asset")] + asset_id: Option, + + /// The amount the payer should send, in asset units. If not specified, it will + /// generate a BIP21 URI/address with no amount. + /// The asset id must also be provided. + #[arg(long)] + amount: Option, }, /// Generates an URL to buy bitcoin from a 3rd party provider BuyBitcoin { @@ -285,7 +295,8 @@ pub(crate) async fn handle_command( Ok(match command { Command::ReceivePayment { payment_method, - payer_amount, + amount_sat, + amount, asset_id, description, use_description_hash, @@ -293,10 +304,10 @@ pub(crate) async fn handle_command( let amount = match asset_id { Some(asset_id) => Some(ReceiveAmount::Asset { asset_id, - payer_amount, + payer_amount: amount, }), None => { - payer_amount.map(|payer_amount_sat| ReceiveAmount::Bitcoin { payer_amount_sat }) + amount_sat.map(|payer_amount_sat| ReceiveAmount::Bitcoin { payer_amount_sat }) } }; let prepare_response = sdk @@ -333,7 +344,7 @@ pub(crate) async fn handle_command( let mut result = command_result!(&response); result.push('\n'); - match parse(&response.destination, None).await? { + match sdk.parse(&response.destination).await? { InputType::Bolt11 { invoice } => result.push_str(&build_qr_text(&invoice.bolt11)), InputType::LiquidAddress { address } => { result.push_str(&build_qr_text(&address.to_uri().map_err(|e| { @@ -361,14 +372,15 @@ pub(crate) async fn handle_command( invoice, offer, address, - receiver_amount, + amount, + amount_sat, asset_id, drain, delay, } => { let destination = match (invoice, offer, address) { (Some(invoice), None, None) => Ok(invoice), - (None, Some(offer), None) => match receiver_amount { + (None, Some(offer), None) => match amount_sat { Some(_) => Ok(offer), None => Err(anyhow!( "Must specify an amount for a BOLT12 offer." @@ -384,15 +396,15 @@ pub(crate) async fn handle_command( "Must specify either a BOLT11 invoice, a BOLT12 offer or a direct/BIP21 address." )) }?; - let amount = match (asset_id, receiver_amount, drain.unwrap_or(false)) { - (Some(asset_id), Some(receiver_amount), _) => Some(PayAmount::Asset { + let amount = match (asset_id, amount, amount_sat, drain.unwrap_or(false)) { + (Some(asset_id), Some(receiver_amount), _, _) => Some(PayAmount::Asset { asset_id, receiver_amount, }), - (None, Some(receiver_amount_sat), _) => Some(PayAmount::Bitcoin { + (None, None, Some(receiver_amount_sat), _) => Some(PayAmount::Bitcoin { receiver_amount_sat, }), - (_, _, true) => Some(PayAmount::Drain), + (_, _, _, true) => Some(PayAmount::Drain), _ => None, }; @@ -717,7 +729,7 @@ pub(crate) async fn handle_command( Command::LnurlAuth { lnurl } => { let lnurl_endpoint = lnurl.trim(); - let res = match parse(lnurl_endpoint, None).await? { + let res = match sdk.parse(lnurl_endpoint).await? { InputType::LnUrlAuth { data: ad } => { let auth_res = sdk.lnurl_auth(ad).await?; serde_json::to_string_pretty(&auth_res).map_err(|e| e.into()) diff --git a/lib/Cargo.lock b/lib/Cargo.lock index 65e092f73..6077b54e7 100644 --- a/lib/Cargo.lock +++ b/lib/Cargo.lock @@ -4147,7 +4147,7 @@ dependencies = [ [[package]] name = "sdk-common" version = "0.6.2" -source = "git+https://github.com/breez/breez-sdk?rev=db5e6dbac3568ebd62b44a879ce5dab30b02d394#db5e6dbac3568ebd62b44a879ce5dab30b02d394" +source = "git+https://github.com/breez/breez-sdk?rev=5a20b38b4c3b574282e8d6e5a61750bb7f4bebed#5a20b38b4c3b574282e8d6e5a61750bb7f4bebed" dependencies = [ "aes 0.8.4", "anyhow", diff --git a/lib/bindings/langs/flutter/breez_sdk_liquid/include/breez_sdk_liquid.h b/lib/bindings/langs/flutter/breez_sdk_liquid/include/breez_sdk_liquid.h index 4c234b430..132d77c3d 100644 --- a/lib/bindings/langs/flutter/breez_sdk_liquid/include/breez_sdk_liquid.h +++ b/lib/bindings/langs/flutter/breez_sdk_liquid/include/breez_sdk_liquid.h @@ -142,6 +142,7 @@ typedef struct wire_cst_liquid_address_data { struct wire_cst_list_prim_u_8_strict *address; int32_t network; struct wire_cst_list_prim_u_8_strict *asset_id; + double *amount; uint64_t *amount_sat; struct wire_cst_list_prim_u_8_strict *label; struct wire_cst_list_prim_u_8_strict *message; @@ -360,7 +361,7 @@ typedef struct wire_cst_PayAmount_Bitcoin { typedef struct wire_cst_PayAmount_Asset { struct wire_cst_list_prim_u_8_strict *asset_id; - uint64_t receiver_amount; + double receiver_amount; } wire_cst_PayAmount_Asset; typedef union PayAmountKind { @@ -384,7 +385,7 @@ typedef struct wire_cst_ReceiveAmount_Bitcoin { typedef struct wire_cst_ReceiveAmount_Asset { struct wire_cst_list_prim_u_8_strict *asset_id; - uint64_t *payer_amount; + double *payer_amount; } wire_cst_ReceiveAmount_Asset; typedef union ReceiveAmountKind { @@ -525,10 +526,17 @@ typedef struct wire_cst_PaymentDetails_Lightning { uint64_t *refund_tx_amount_sat; } wire_cst_PaymentDetails_Lightning; +typedef struct wire_cst_asset_info { + struct wire_cst_list_prim_u_8_strict *name; + struct wire_cst_list_prim_u_8_strict *ticker; + double amount; +} wire_cst_asset_info; + typedef struct wire_cst_PaymentDetails_Liquid { struct wire_cst_list_prim_u_8_strict *destination; struct wire_cst_list_prim_u_8_strict *description; struct wire_cst_list_prim_u_8_strict *asset_id; + struct wire_cst_asset_info *asset_info; } wire_cst_PaymentDetails_Liquid; typedef struct wire_cst_PaymentDetails_Bitcoin { @@ -624,6 +632,18 @@ typedef struct wire_cst_list_external_input_parser { int32_t len; } wire_cst_list_external_input_parser; +typedef struct wire_cst_asset_metadata { + struct wire_cst_list_prim_u_8_strict *asset_id; + struct wire_cst_list_prim_u_8_strict *name; + struct wire_cst_list_prim_u_8_strict *ticker; + uint8_t precision; +} wire_cst_asset_metadata; + +typedef struct wire_cst_list_asset_metadata { + struct wire_cst_asset_metadata *ptr; + int32_t len; +} wire_cst_list_asset_metadata; + typedef struct wire_cst_config { struct wire_cst_list_prim_u_8_strict *liquid_electrum_url; struct wire_cst_list_prim_u_8_strict *bitcoin_electrum_url; @@ -639,6 +659,7 @@ typedef struct wire_cst_config { struct wire_cst_list_external_input_parser *external_input_parsers; bool use_default_external_input_parsers; uint32_t *onchain_fee_rate_leeway_sat_per_vbyte; + struct wire_cst_list_asset_metadata *asset_metadata; } wire_cst_config; typedef struct wire_cst_connect_request { @@ -681,7 +702,10 @@ typedef struct wire_cst_symbol { typedef struct wire_cst_asset_balance { struct wire_cst_list_prim_u_8_strict *asset_id; - uint64_t balance; + uint64_t balance_sat; + struct wire_cst_list_prim_u_8_strict *name; + struct wire_cst_list_prim_u_8_strict *ticker; + double *balance; } wire_cst_asset_balance; typedef struct wire_cst_list_asset_balance { @@ -1046,6 +1070,10 @@ typedef struct wire_cst_PaymentError_AmountMissing { struct wire_cst_list_prim_u_8_strict *err; } wire_cst_PaymentError_AmountMissing; +typedef struct wire_cst_PaymentError_AssetError { + struct wire_cst_list_prim_u_8_strict *err; +} wire_cst_PaymentError_AssetError; + typedef struct wire_cst_PaymentError_InvalidNetwork { struct wire_cst_list_prim_u_8_strict *err; } wire_cst_PaymentError_InvalidNetwork; @@ -1085,6 +1113,7 @@ typedef struct wire_cst_PaymentError_SignerError { typedef union PaymentErrorKind { struct wire_cst_PaymentError_AmountMissing AmountMissing; + struct wire_cst_PaymentError_AssetError AssetError; struct wire_cst_PaymentError_InvalidNetwork InvalidNetwork; struct wire_cst_PaymentError_Generic Generic; struct wire_cst_PaymentError_InvalidDescription InvalidDescription; @@ -1309,6 +1338,8 @@ struct wire_cst_aes_success_action_data_result *frbgen_breez_liquid_cst_new_box_ struct wire_cst_amount *frbgen_breez_liquid_cst_new_box_autoadd_amount(void); +struct wire_cst_asset_info *frbgen_breez_liquid_cst_new_box_autoadd_asset_info(void); + struct wire_cst_backup_request *frbgen_breez_liquid_cst_new_box_autoadd_backup_request(void); struct wire_cst_binding_event_listener *frbgen_breez_liquid_cst_new_box_autoadd_binding_event_listener(void); @@ -1411,6 +1442,8 @@ struct wire_cst_list_String *frbgen_breez_liquid_cst_new_list_String(int32_t len struct wire_cst_list_asset_balance *frbgen_breez_liquid_cst_new_list_asset_balance(int32_t len); +struct wire_cst_list_asset_metadata *frbgen_breez_liquid_cst_new_list_asset_metadata(int32_t len); + struct wire_cst_list_external_input_parser *frbgen_breez_liquid_cst_new_list_external_input_parser(int32_t len); struct wire_cst_list_fiat_currency *frbgen_breez_liquid_cst_new_list_fiat_currency(int32_t len); @@ -1443,6 +1476,7 @@ static int64_t dummy_method_to_enforce_bundling(void) { dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_aes_success_action_data_decrypted); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_aes_success_action_data_result); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_amount); + dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_asset_info); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_backup_request); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_binding_event_listener); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_bitcoin_address_data); @@ -1494,6 +1528,7 @@ static int64_t dummy_method_to_enforce_bundling(void) { dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_url_success_action_data); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_list_String); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_list_asset_balance); + dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_list_asset_metadata); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_list_external_input_parser); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_list_fiat_currency); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_list_ln_offer_blinded_path); diff --git a/lib/bindings/src/breez_sdk_liquid.udl b/lib/bindings/src/breez_sdk_liquid.udl index 417d36e25..01192c165 100644 --- a/lib/bindings/src/breez_sdk_liquid.udl +++ b/lib/bindings/src/breez_sdk_liquid.udl @@ -85,6 +85,7 @@ dictionary LiquidAddressData { string address; Network network; string? asset_id; + f64? amount; u64? amount_sat; string? label; string? message; @@ -306,6 +307,7 @@ enum PaymentError { "PaymentInProgress", "AmountOutOfRange", "AmountMissing", + "AssetError", "Generic", "InvalidOrExpiredFees", "InsufficientFunds", @@ -339,6 +341,7 @@ dictionary Config { boolean use_default_external_input_parsers = true; sequence? external_input_parsers = null; u32? onchain_fee_rate_leeway_sat_per_vbyte = null; + sequence? asset_metadata = null; }; enum LiquidNetwork { @@ -357,7 +360,10 @@ dictionary ConnectWithSignerRequest { dictionary AssetBalance { string asset_id; - u64 balance; + u64 balance_sat; + string? name; + string? ticker; + f64? balance; }; dictionary BlockchainInfo { @@ -450,7 +456,7 @@ enum PaymentMethod { [Enum] interface ReceiveAmount { Bitcoin(u64 payer_amount_sat); - Asset(string asset_id, u64? payer_amount); + Asset(string asset_id, f64? payer_amount); }; dictionary PrepareReceiveRequest { @@ -496,7 +502,7 @@ dictionary OnchainPaymentLimitsResponse { [Enum] interface PayAmount { Bitcoin(u64 receiver_amount_sat); - Asset(string asset_id, u64 receiver_amount); + Asset(string asset_id, f64 receiver_amount); Drain(); }; @@ -591,10 +597,16 @@ dictionary LnUrlInfo { string? lnurl_withdraw_endpoint; }; +dictionary AssetInfo { + string name; + string ticker; + f64 amount; +}; + [Enum] interface PaymentDetails { Lightning(string swap_id, string description, u32 liquid_expiration_blockheight, string? preimage, string? invoice, string? bolt12_offer, string? payment_hash, string? destination_pubkey, LnUrlInfo? lnurl_info, string? refund_tx_id, u64? refund_tx_amount_sat); - Liquid(string asset_id, string destination, string description); + Liquid(string asset_id, string destination, string description, AssetInfo? asset_info); Bitcoin(string swap_id, string description, boolean auto_accepted_fees, u32? bitcoin_expiration_blockheight, u32? liquid_expiration_blockheight, string? refund_tx_id, u64? refund_tx_amount_sat); }; @@ -696,6 +708,13 @@ dictionary ExternalInputParser { string parser_url; }; +dictionary AssetMetadata { + string asset_id; + string name; + string ticker; + u8 precision; +}; + namespace breez_sdk_liquid { [Throws=SdkError] BindingLiquidSdk connect(ConnectRequest req); diff --git a/lib/core/Cargo.toml b/lib/core/Cargo.toml index 11b68bb2c..bb37b13eb 100644 --- a/lib/core/Cargo.toml +++ b/lib/core/Cargo.toml @@ -31,7 +31,7 @@ lwk_wollet = { git = "https://github.com/dangeross/lwk", branch = "savage-full-s #lwk_wollet = "0.8.0" rusqlite = { version = "0.31", features = ["backup", "bundled"] } rusqlite_migration = "1.0" -sdk-common = { git = "https://github.com/breez/breez-sdk", rev = "db5e6dbac3568ebd62b44a879ce5dab30b02d394", features = ["liquid"] } +sdk-common = { git = "https://github.com/breez/breez-sdk", rev = "5a20b38b4c3b574282e8d6e5a61750bb7f4bebed", features = ["liquid"] } serde = { version = "1.0.197", features = ["derive"] } serde_json = "1.0.116" strum = "0.25" diff --git a/lib/core/src/bindings.rs b/lib/core/src/bindings.rs index 2b2753575..ecb4d08de 100644 --- a/lib/core/src/bindings.rs +++ b/lib/core/src/bindings.rs @@ -406,6 +406,7 @@ pub struct _LiquidAddressData { pub address: String, pub network: Network, pub asset_id: Option, + pub amount: Option, pub amount_sat: Option, pub label: Option, pub message: Option, diff --git a/lib/core/src/error.rs b/lib/core/src/error.rs index b93969b0e..bf2ce67b4 100644 --- a/lib/core/src/error.rs +++ b/lib/core/src/error.rs @@ -76,6 +76,9 @@ pub enum PaymentError { #[error("Amount is missing: {err}")] AmountMissing { err: String }, + #[error("Asset error: {err}")] + AssetError { err: String }, + #[error("Invalid network: {err}")] InvalidNetwork { err: String }, @@ -125,6 +128,12 @@ pub enum PaymentError { SignerError { err: String }, } impl PaymentError { + pub(crate) fn asset_error(err: &str) -> Self { + Self::AssetError { + err: err.to_string(), + } + } + pub(crate) fn generic(err: &str) -> Self { Self::Generic { err: err.to_string(), diff --git a/lib/core/src/frb_generated.rs b/lib/core/src/frb_generated.rs index 2eab08977..01d8439dc 100644 --- a/lib/core/src/frb_generated.rs +++ b/lib/core/src/frb_generated.rs @@ -1966,6 +1966,7 @@ const _: fn() = || { let _: String = LiquidAddressData.address; let _: crate::bindings::Network = LiquidAddressData.network; let _: Option = LiquidAddressData.asset_id; + let _: Option = LiquidAddressData.amount; let _: Option = LiquidAddressData.amount_sat; let _: Option = LiquidAddressData.label; let _: Option = LiquidAddressData.message; @@ -2383,14 +2384,50 @@ impl SseDecode for crate::model::AssetBalance { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { let mut var_assetId = ::sse_decode(deserializer); - let mut var_balance = ::sse_decode(deserializer); + let mut var_balanceSat = ::sse_decode(deserializer); + let mut var_name = >::sse_decode(deserializer); + let mut var_ticker = >::sse_decode(deserializer); + let mut var_balance = >::sse_decode(deserializer); return crate::model::AssetBalance { asset_id: var_assetId, + balance_sat: var_balanceSat, + name: var_name, + ticker: var_ticker, balance: var_balance, }; } } +impl SseDecode for crate::model::AssetInfo { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut var_name = ::sse_decode(deserializer); + let mut var_ticker = ::sse_decode(deserializer); + let mut var_amount = ::sse_decode(deserializer); + return crate::model::AssetInfo { + name: var_name, + ticker: var_ticker, + amount: var_amount, + }; + } +} + +impl SseDecode for crate::model::AssetMetadata { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut var_assetId = ::sse_decode(deserializer); + let mut var_name = ::sse_decode(deserializer); + let mut var_ticker = ::sse_decode(deserializer); + let mut var_precision = ::sse_decode(deserializer); + return crate::model::AssetMetadata { + asset_id: var_assetId, + name: var_name, + ticker: var_ticker, + precision: var_precision, + }; + } +} + impl SseDecode for crate::model::BackupRequest { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -2515,6 +2552,8 @@ impl SseDecode for crate::model::Config { >>::sse_decode(deserializer); let mut var_useDefaultExternalInputParsers = ::sse_decode(deserializer); let mut var_onchainFeeRateLeewaySatPerVbyte = >::sse_decode(deserializer); + let mut var_assetMetadata = + >>::sse_decode(deserializer); return crate::model::Config { liquid_electrum_url: var_liquidElectrumUrl, bitcoin_electrum_url: var_bitcoinElectrumUrl, @@ -2530,6 +2569,7 @@ impl SseDecode for crate::model::Config { external_input_parsers: var_externalInputParsers, use_default_external_input_parsers: var_useDefaultExternalInputParsers, onchain_fee_rate_leeway_sat_per_vbyte: var_onchainFeeRateLeewaySatPerVbyte, + asset_metadata: var_assetMetadata, }; } } @@ -2768,6 +2808,7 @@ impl SseDecode for crate::bindings::LiquidAddressData { let mut var_address = ::sse_decode(deserializer); let mut var_network = ::sse_decode(deserializer); let mut var_assetId = >::sse_decode(deserializer); + let mut var_amount = >::sse_decode(deserializer); let mut var_amountSat = >::sse_decode(deserializer); let mut var_label = >::sse_decode(deserializer); let mut var_message = >::sse_decode(deserializer); @@ -2775,6 +2816,7 @@ impl SseDecode for crate::bindings::LiquidAddressData { address: var_address, network: var_network, asset_id: var_assetId, + amount: var_amount, amount_sat: var_amountSat, label: var_label, message: var_message, @@ -2818,6 +2860,18 @@ impl SseDecode for Vec { } } +impl SseDecode for Vec { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut len_ = ::sse_decode(deserializer); + let mut ans_ = vec![]; + for idx_ in 0..len_ { + ans_.push(::sse_decode(deserializer)); + } + return ans_; + } +} + impl SseDecode for Vec { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -3556,6 +3610,17 @@ impl SseDecode for Option { } } +impl SseDecode for Option { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + if (::sse_decode(deserializer)) { + return Some(::sse_decode(deserializer)); + } else { + return None; + } + } +} + impl SseDecode for Option { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -3701,6 +3766,17 @@ impl SseDecode for Option { } } +impl SseDecode for Option> { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + if (::sse_decode(deserializer)) { + return Some(>::sse_decode(deserializer)); + } else { + return None; + } + } +} + impl SseDecode for Option> { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -3749,7 +3825,7 @@ impl SseDecode for crate::model::PayAmount { } 1 => { let mut var_assetId = ::sse_decode(deserializer); - let mut var_receiverAmount = ::sse_decode(deserializer); + let mut var_receiverAmount = ::sse_decode(deserializer); return crate::model::PayAmount::Asset { asset_id: var_assetId, receiver_amount: var_receiverAmount, @@ -3841,10 +3917,12 @@ impl SseDecode for crate::model::PaymentDetails { let mut var_destination = ::sse_decode(deserializer); let mut var_description = ::sse_decode(deserializer); let mut var_assetId = ::sse_decode(deserializer); + let mut var_assetInfo = >::sse_decode(deserializer); return crate::model::PaymentDetails::Liquid { destination: var_destination, description: var_description, asset_id: var_assetId, + asset_info: var_assetInfo, }; } 2 => { @@ -3895,47 +3973,51 @@ impl SseDecode for crate::error::PaymentError { } 5 => { let mut var_err = ::sse_decode(deserializer); - return crate::error::PaymentError::InvalidNetwork { err: var_err }; + return crate::error::PaymentError::AssetError { err: var_err }; } 6 => { let mut var_err = ::sse_decode(deserializer); - return crate::error::PaymentError::Generic { err: var_err }; + return crate::error::PaymentError::InvalidNetwork { err: var_err }; } 7 => { - return crate::error::PaymentError::InvalidOrExpiredFees; + let mut var_err = ::sse_decode(deserializer); + return crate::error::PaymentError::Generic { err: var_err }; } 8 => { - return crate::error::PaymentError::InsufficientFunds; + return crate::error::PaymentError::InvalidOrExpiredFees; } 9 => { + return crate::error::PaymentError::InsufficientFunds; + } + 10 => { let mut var_err = ::sse_decode(deserializer); return crate::error::PaymentError::InvalidDescription { err: var_err }; } - 10 => { + 11 => { let mut var_err = ::sse_decode(deserializer); return crate::error::PaymentError::InvalidInvoice { err: var_err }; } - 11 => { + 12 => { return crate::error::PaymentError::InvalidPreimage; } - 12 => { + 13 => { let mut var_err = ::sse_decode(deserializer); return crate::error::PaymentError::LwkError { err: var_err }; } - 13 => { + 14 => { return crate::error::PaymentError::PairsNotFound; } - 14 => { + 15 => { return crate::error::PaymentError::PaymentTimeout; } - 15 => { + 16 => { return crate::error::PaymentError::PersistError; } - 16 => { + 17 => { let mut var_err = ::sse_decode(deserializer); return crate::error::PaymentError::ReceiveError { err: var_err }; } - 17 => { + 18 => { let mut var_err = ::sse_decode(deserializer); let mut var_refundTxId = ::sse_decode(deserializer); return crate::error::PaymentError::Refunded { @@ -3943,14 +4025,14 @@ impl SseDecode for crate::error::PaymentError { refund_tx_id: var_refundTxId, }; } - 18 => { + 19 => { return crate::error::PaymentError::SelfTransferNotSupported; } - 19 => { + 20 => { let mut var_err = ::sse_decode(deserializer); return crate::error::PaymentError::SendError { err: var_err }; } - 20 => { + 21 => { let mut var_err = ::sse_decode(deserializer); return crate::error::PaymentError::SignerError { err: var_err }; } @@ -4200,7 +4282,7 @@ impl SseDecode for crate::model::ReceiveAmount { } 1 => { let mut var_assetId = ::sse_decode(deserializer); - let mut var_payerAmount = >::sse_decode(deserializer); + let mut var_payerAmount = >::sse_decode(deserializer); return crate::model::ReceiveAmount::Asset { asset_id: var_assetId, payer_amount: var_payerAmount, @@ -4809,6 +4891,9 @@ impl flutter_rust_bridge::IntoDart for crate::model::AssetBalance { fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { [ self.asset_id.into_into_dart().into_dart(), + self.balance_sat.into_into_dart().into_dart(), + self.name.into_into_dart().into_dart(), + self.ticker.into_into_dart().into_dart(), self.balance.into_into_dart().into_dart(), ] .into_dart() @@ -4821,6 +4906,43 @@ impl flutter_rust_bridge::IntoIntoDart for crate::mo } } // Codec=Dco (DartCObject based), see doc to use other codecs +impl flutter_rust_bridge::IntoDart for crate::model::AssetInfo { + fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { + [ + self.name.into_into_dart().into_dart(), + self.ticker.into_into_dart().into_dart(), + self.amount.into_into_dart().into_dart(), + ] + .into_dart() + } +} +impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive for crate::model::AssetInfo {} +impl flutter_rust_bridge::IntoIntoDart for crate::model::AssetInfo { + fn into_into_dart(self) -> crate::model::AssetInfo { + self + } +} +// Codec=Dco (DartCObject based), see doc to use other codecs +impl flutter_rust_bridge::IntoDart for crate::model::AssetMetadata { + fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { + [ + self.asset_id.into_into_dart().into_dart(), + self.name.into_into_dart().into_dart(), + self.ticker.into_into_dart().into_dart(), + self.precision.into_into_dart().into_dart(), + ] + .into_dart() + } +} +impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive for crate::model::AssetMetadata {} +impl flutter_rust_bridge::IntoIntoDart + for crate::model::AssetMetadata +{ + fn into_into_dart(self) -> crate::model::AssetMetadata { + self + } +} +// Codec=Dco (DartCObject based), see doc to use other codecs impl flutter_rust_bridge::IntoDart for crate::model::BackupRequest { fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { [self.backup_path.into_into_dart().into_dart()].into_dart() @@ -4997,6 +5119,7 @@ impl flutter_rust_bridge::IntoDart for crate::model::Config { self.onchain_fee_rate_leeway_sat_per_vbyte .into_into_dart() .into_dart(), + self.asset_metadata.into_into_dart().into_dart(), ] .into_dart() } @@ -5272,6 +5395,7 @@ impl flutter_rust_bridge::IntoDart for FrbWrapper [ 1.into_dart(), destination.into_into_dart().into_dart(), description.into_into_dart().into_dart(), asset_id.into_into_dart().into_dart(), + asset_info.into_into_dart().into_dart(), ] .into_dart(), crate::model::PaymentDetails::Bitcoin { @@ -6146,42 +6272,45 @@ impl flutter_rust_bridge::IntoDart for crate::error::PaymentError { crate::error::PaymentError::AmountMissing { err } => { [4.into_dart(), err.into_into_dart().into_dart()].into_dart() } - crate::error::PaymentError::InvalidNetwork { err } => { + crate::error::PaymentError::AssetError { err } => { [5.into_dart(), err.into_into_dart().into_dart()].into_dart() } - crate::error::PaymentError::Generic { err } => { + crate::error::PaymentError::InvalidNetwork { err } => { [6.into_dart(), err.into_into_dart().into_dart()].into_dart() } - crate::error::PaymentError::InvalidOrExpiredFees => [7.into_dart()].into_dart(), - crate::error::PaymentError::InsufficientFunds => [8.into_dart()].into_dart(), + crate::error::PaymentError::Generic { err } => { + [7.into_dart(), err.into_into_dart().into_dart()].into_dart() + } + crate::error::PaymentError::InvalidOrExpiredFees => [8.into_dart()].into_dart(), + crate::error::PaymentError::InsufficientFunds => [9.into_dart()].into_dart(), crate::error::PaymentError::InvalidDescription { err } => { - [9.into_dart(), err.into_into_dart().into_dart()].into_dart() + [10.into_dart(), err.into_into_dart().into_dart()].into_dart() } crate::error::PaymentError::InvalidInvoice { err } => { - [10.into_dart(), err.into_into_dart().into_dart()].into_dart() + [11.into_dart(), err.into_into_dart().into_dart()].into_dart() } - crate::error::PaymentError::InvalidPreimage => [11.into_dart()].into_dart(), + crate::error::PaymentError::InvalidPreimage => [12.into_dart()].into_dart(), crate::error::PaymentError::LwkError { err } => { - [12.into_dart(), err.into_into_dart().into_dart()].into_dart() + [13.into_dart(), err.into_into_dart().into_dart()].into_dart() } - crate::error::PaymentError::PairsNotFound => [13.into_dart()].into_dart(), - crate::error::PaymentError::PaymentTimeout => [14.into_dart()].into_dart(), - crate::error::PaymentError::PersistError => [15.into_dart()].into_dart(), + crate::error::PaymentError::PairsNotFound => [14.into_dart()].into_dart(), + crate::error::PaymentError::PaymentTimeout => [15.into_dart()].into_dart(), + crate::error::PaymentError::PersistError => [16.into_dart()].into_dart(), crate::error::PaymentError::ReceiveError { err } => { - [16.into_dart(), err.into_into_dart().into_dart()].into_dart() + [17.into_dart(), err.into_into_dart().into_dart()].into_dart() } crate::error::PaymentError::Refunded { err, refund_tx_id } => [ - 17.into_dart(), + 18.into_dart(), err.into_into_dart().into_dart(), refund_tx_id.into_into_dart().into_dart(), ] .into_dart(), - crate::error::PaymentError::SelfTransferNotSupported => [18.into_dart()].into_dart(), + crate::error::PaymentError::SelfTransferNotSupported => [19.into_dart()].into_dart(), crate::error::PaymentError::SendError { err } => { - [19.into_dart(), err.into_into_dart().into_dart()].into_dart() + [20.into_dart(), err.into_into_dart().into_dart()].into_dart() } crate::error::PaymentError::SignerError { err } => { - [20.into_dart(), err.into_into_dart().into_dart()].into_dart() + [21.into_dart(), err.into_into_dart().into_dart()].into_dart() } _ => { unimplemented!(""); @@ -7155,7 +7284,29 @@ impl SseEncode for crate::model::AssetBalance { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { ::sse_encode(self.asset_id, serializer); - ::sse_encode(self.balance, serializer); + ::sse_encode(self.balance_sat, serializer); + >::sse_encode(self.name, serializer); + >::sse_encode(self.ticker, serializer); + >::sse_encode(self.balance, serializer); + } +} + +impl SseEncode for crate::model::AssetInfo { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.name, serializer); + ::sse_encode(self.ticker, serializer); + ::sse_encode(self.amount, serializer); + } +} + +impl SseEncode for crate::model::AssetMetadata { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.asset_id, serializer); + ::sse_encode(self.name, serializer); + ::sse_encode(self.ticker, serializer); + ::sse_encode(self.precision, serializer); } } @@ -7258,6 +7409,7 @@ impl SseEncode for crate::model::Config { ); ::sse_encode(self.use_default_external_input_parsers, serializer); >::sse_encode(self.onchain_fee_rate_leeway_sat_per_vbyte, serializer); + >>::sse_encode(self.asset_metadata, serializer); } } @@ -7434,6 +7586,7 @@ impl SseEncode for crate::bindings::LiquidAddressData { ::sse_encode(self.address, serializer); ::sse_encode(self.network, serializer); >::sse_encode(self.asset_id, serializer); + >::sse_encode(self.amount, serializer); >::sse_encode(self.amount_sat, serializer); >::sse_encode(self.label, serializer); >::sse_encode(self.message, serializer); @@ -7476,6 +7629,16 @@ impl SseEncode for Vec { } } +impl SseEncode for Vec { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.len() as _, serializer); + for item in self { + ::sse_encode(item, serializer); + } + } +} + impl SseEncode for Vec { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -8048,6 +8211,16 @@ impl SseEncode for Option { } } +impl SseEncode for Option { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.is_some(), serializer); + if let Some(value) = self { + ::sse_encode(value, serializer); + } + } +} + impl SseEncode for Option { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -8178,6 +8351,16 @@ impl SseEncode for Option { } } +impl SseEncode for Option> { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.is_some(), serializer); + if let Some(value) = self { + >::sse_encode(value, serializer); + } + } +} + impl SseEncode for Option> { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -8224,7 +8407,7 @@ impl SseEncode for crate::model::PayAmount { } => { ::sse_encode(1, serializer); ::sse_encode(asset_id, serializer); - ::sse_encode(receiver_amount, serializer); + ::sse_encode(receiver_amount, serializer); } crate::model::PayAmount::Drain => { ::sse_encode(2, serializer); @@ -8294,11 +8477,13 @@ impl SseEncode for crate::model::PaymentDetails { destination, description, asset_id, + asset_info, } => { ::sse_encode(1, serializer); ::sse_encode(destination, serializer); ::sse_encode(description, serializer); ::sse_encode(asset_id, serializer); + >::sse_encode(asset_info, serializer); } crate::model::PaymentDetails::Bitcoin { swap_id, @@ -8345,62 +8530,66 @@ impl SseEncode for crate::error::PaymentError { ::sse_encode(4, serializer); ::sse_encode(err, serializer); } - crate::error::PaymentError::InvalidNetwork { err } => { + crate::error::PaymentError::AssetError { err } => { ::sse_encode(5, serializer); ::sse_encode(err, serializer); } - crate::error::PaymentError::Generic { err } => { + crate::error::PaymentError::InvalidNetwork { err } => { ::sse_encode(6, serializer); ::sse_encode(err, serializer); } - crate::error::PaymentError::InvalidOrExpiredFees => { + crate::error::PaymentError::Generic { err } => { ::sse_encode(7, serializer); + ::sse_encode(err, serializer); } - crate::error::PaymentError::InsufficientFunds => { + crate::error::PaymentError::InvalidOrExpiredFees => { ::sse_encode(8, serializer); } - crate::error::PaymentError::InvalidDescription { err } => { + crate::error::PaymentError::InsufficientFunds => { ::sse_encode(9, serializer); + } + crate::error::PaymentError::InvalidDescription { err } => { + ::sse_encode(10, serializer); ::sse_encode(err, serializer); } crate::error::PaymentError::InvalidInvoice { err } => { - ::sse_encode(10, serializer); + ::sse_encode(11, serializer); ::sse_encode(err, serializer); } crate::error::PaymentError::InvalidPreimage => { - ::sse_encode(11, serializer); + ::sse_encode(12, serializer); } crate::error::PaymentError::LwkError { err } => { - ::sse_encode(12, serializer); + ::sse_encode(13, serializer); ::sse_encode(err, serializer); } crate::error::PaymentError::PairsNotFound => { - ::sse_encode(13, serializer); + ::sse_encode(14, serializer); } crate::error::PaymentError::PaymentTimeout => { - ::sse_encode(14, serializer); + ::sse_encode(15, serializer); } crate::error::PaymentError::PersistError => { - ::sse_encode(15, serializer); + ::sse_encode(16, serializer); } crate::error::PaymentError::ReceiveError { err } => { - ::sse_encode(16, serializer); + ::sse_encode(17, serializer); ::sse_encode(err, serializer); } crate::error::PaymentError::Refunded { err, refund_tx_id } => { - ::sse_encode(17, serializer); + ::sse_encode(18, serializer); ::sse_encode(err, serializer); ::sse_encode(refund_tx_id, serializer); } crate::error::PaymentError::SelfTransferNotSupported => { - ::sse_encode(18, serializer); + ::sse_encode(19, serializer); } crate::error::PaymentError::SendError { err } => { - ::sse_encode(19, serializer); + ::sse_encode(20, serializer); ::sse_encode(err, serializer); } crate::error::PaymentError::SignerError { err } => { - ::sse_encode(20, serializer); + ::sse_encode(21, serializer); ::sse_encode(err, serializer); } _ => { @@ -8596,7 +8785,7 @@ impl SseEncode for crate::model::ReceiveAmount { } => { ::sse_encode(1, serializer); ::sse_encode(asset_id, serializer); - >::sse_encode(payer_amount, serializer); + >::sse_encode(payer_amount, serializer); } _ => { unimplemented!(""); @@ -9098,10 +9287,34 @@ mod io { fn cst_decode(self) -> crate::model::AssetBalance { crate::model::AssetBalance { asset_id: self.asset_id.cst_decode(), + balance_sat: self.balance_sat.cst_decode(), + name: self.name.cst_decode(), + ticker: self.ticker.cst_decode(), balance: self.balance.cst_decode(), } } } + impl CstDecode for wire_cst_asset_info { + // Codec=Cst (C-struct based), see doc to use other codecs + fn cst_decode(self) -> crate::model::AssetInfo { + crate::model::AssetInfo { + name: self.name.cst_decode(), + ticker: self.ticker.cst_decode(), + amount: self.amount.cst_decode(), + } + } + } + impl CstDecode for wire_cst_asset_metadata { + // Codec=Cst (C-struct based), see doc to use other codecs + fn cst_decode(self) -> crate::model::AssetMetadata { + crate::model::AssetMetadata { + asset_id: self.asset_id.cst_decode(), + name: self.name.cst_decode(), + ticker: self.ticker.cst_decode(), + precision: self.precision.cst_decode(), + } + } + } impl CstDecode for wire_cst_backup_request { // Codec=Cst (C-struct based), see doc to use other codecs fn cst_decode(self) -> crate::model::BackupRequest { @@ -9180,6 +9393,13 @@ mod io { CstDecode::::cst_decode(*wrap).into() } } + impl CstDecode for *mut wire_cst_asset_info { + // Codec=Cst (C-struct based), see doc to use other codecs + fn cst_decode(self) -> crate::model::AssetInfo { + let wrap = unsafe { flutter_rust_bridge::for_generated::box_from_leak_ptr(self) }; + CstDecode::::cst_decode(*wrap).into() + } + } impl CstDecode for *mut wire_cst_backup_request { // Codec=Cst (C-struct based), see doc to use other codecs fn cst_decode(self) -> crate::model::BackupRequest { @@ -9580,6 +9800,7 @@ mod io { onchain_fee_rate_leeway_sat_per_vbyte: self .onchain_fee_rate_leeway_sat_per_vbyte .cst_decode(), + asset_metadata: self.asset_metadata.cst_decode(), } } } @@ -9767,6 +9988,7 @@ mod io { address: self.address.cst_decode(), network: self.network.cst_decode(), asset_id: self.asset_id.cst_decode(), + amount: self.amount.cst_decode(), amount_sat: self.amount_sat.cst_decode(), label: self.label.cst_decode(), message: self.message.cst_decode(), @@ -9793,6 +10015,16 @@ mod io { vec.into_iter().map(CstDecode::cst_decode).collect() } } + impl CstDecode> for *mut wire_cst_list_asset_metadata { + // Codec=Cst (C-struct based), see doc to use other codecs + fn cst_decode(self) -> Vec { + let vec = unsafe { + let wrap = flutter_rust_bridge::for_generated::box_from_leak_ptr(self); + flutter_rust_bridge::for_generated::vec_from_leak_ptr(wrap.ptr, wrap.len) + }; + vec.into_iter().map(CstDecode::cst_decode).collect() + } + } impl CstDecode> for *mut wire_cst_list_external_input_parser { @@ -10454,6 +10686,7 @@ mod io { destination: ans.destination.cst_decode(), description: ans.description.cst_decode(), asset_id: ans.asset_id.cst_decode(), + asset_info: ans.asset_info.cst_decode(), } } 2 => { @@ -10491,62 +10724,68 @@ mod io { } } 5 => { + let ans = unsafe { self.kind.AssetError }; + crate::error::PaymentError::AssetError { + err: ans.err.cst_decode(), + } + } + 6 => { let ans = unsafe { self.kind.InvalidNetwork }; crate::error::PaymentError::InvalidNetwork { err: ans.err.cst_decode(), } } - 6 => { + 7 => { let ans = unsafe { self.kind.Generic }; crate::error::PaymentError::Generic { err: ans.err.cst_decode(), } } - 7 => crate::error::PaymentError::InvalidOrExpiredFees, - 8 => crate::error::PaymentError::InsufficientFunds, - 9 => { + 8 => crate::error::PaymentError::InvalidOrExpiredFees, + 9 => crate::error::PaymentError::InsufficientFunds, + 10 => { let ans = unsafe { self.kind.InvalidDescription }; crate::error::PaymentError::InvalidDescription { err: ans.err.cst_decode(), } } - 10 => { + 11 => { let ans = unsafe { self.kind.InvalidInvoice }; crate::error::PaymentError::InvalidInvoice { err: ans.err.cst_decode(), } } - 11 => crate::error::PaymentError::InvalidPreimage, - 12 => { + 12 => crate::error::PaymentError::InvalidPreimage, + 13 => { let ans = unsafe { self.kind.LwkError }; crate::error::PaymentError::LwkError { err: ans.err.cst_decode(), } } - 13 => crate::error::PaymentError::PairsNotFound, - 14 => crate::error::PaymentError::PaymentTimeout, - 15 => crate::error::PaymentError::PersistError, - 16 => { + 14 => crate::error::PaymentError::PairsNotFound, + 15 => crate::error::PaymentError::PaymentTimeout, + 16 => crate::error::PaymentError::PersistError, + 17 => { let ans = unsafe { self.kind.ReceiveError }; crate::error::PaymentError::ReceiveError { err: ans.err.cst_decode(), } } - 17 => { + 18 => { let ans = unsafe { self.kind.Refunded }; crate::error::PaymentError::Refunded { err: ans.err.cst_decode(), refund_tx_id: ans.refund_tx_id.cst_decode(), } } - 18 => crate::error::PaymentError::SelfTransferNotSupported, - 19 => { + 19 => crate::error::PaymentError::SelfTransferNotSupported, + 20 => { let ans = unsafe { self.kind.SendError }; crate::error::PaymentError::SendError { err: ans.err.cst_decode(), } } - 20 => { + 21 => { let ans = unsafe { self.kind.SignerError }; crate::error::PaymentError::SignerError { err: ans.err.cst_decode(), @@ -11089,7 +11328,10 @@ mod io { fn new_with_null_ptr() -> Self { Self { asset_id: core::ptr::null_mut(), - balance: Default::default(), + balance_sat: Default::default(), + name: core::ptr::null_mut(), + ticker: core::ptr::null_mut(), + balance: core::ptr::null_mut(), } } } @@ -11098,6 +11340,35 @@ mod io { Self::new_with_null_ptr() } } + impl NewWithNullPtr for wire_cst_asset_info { + fn new_with_null_ptr() -> Self { + Self { + name: core::ptr::null_mut(), + ticker: core::ptr::null_mut(), + amount: Default::default(), + } + } + } + impl Default for wire_cst_asset_info { + fn default() -> Self { + Self::new_with_null_ptr() + } + } + impl NewWithNullPtr for wire_cst_asset_metadata { + fn new_with_null_ptr() -> Self { + Self { + asset_id: core::ptr::null_mut(), + name: core::ptr::null_mut(), + ticker: core::ptr::null_mut(), + precision: Default::default(), + } + } + } + impl Default for wire_cst_asset_metadata { + fn default() -> Self { + Self::new_with_null_ptr() + } + } impl NewWithNullPtr for wire_cst_backup_request { fn new_with_null_ptr() -> Self { Self { @@ -11207,6 +11478,7 @@ mod io { external_input_parsers: core::ptr::null_mut(), use_default_external_input_parsers: Default::default(), onchain_fee_rate_leeway_sat_per_vbyte: core::ptr::null_mut(), + asset_metadata: core::ptr::null_mut(), } } } @@ -11372,6 +11644,7 @@ mod io { address: core::ptr::null_mut(), network: Default::default(), asset_id: core::ptr::null_mut(), + amount: core::ptr::null_mut(), amount_sat: core::ptr::null_mut(), label: core::ptr::null_mut(), message: core::ptr::null_mut(), @@ -12721,6 +12994,14 @@ mod io { flutter_rust_bridge::for_generated::new_leak_box_ptr(wire_cst_amount::new_with_null_ptr()) } + #[no_mangle] + pub extern "C" fn frbgen_breez_liquid_cst_new_box_autoadd_asset_info( + ) -> *mut wire_cst_asset_info { + flutter_rust_bridge::for_generated::new_leak_box_ptr( + wire_cst_asset_info::new_with_null_ptr(), + ) + } + #[no_mangle] pub extern "C" fn frbgen_breez_liquid_cst_new_box_autoadd_backup_request( ) -> *mut wire_cst_backup_request { @@ -13115,6 +13396,20 @@ mod io { flutter_rust_bridge::for_generated::new_leak_box_ptr(wrap) } + #[no_mangle] + pub extern "C" fn frbgen_breez_liquid_cst_new_list_asset_metadata( + len: i32, + ) -> *mut wire_cst_list_asset_metadata { + let wrap = wire_cst_list_asset_metadata { + ptr: flutter_rust_bridge::for_generated::new_leak_vec_ptr( + ::new_with_null_ptr(), + len, + ), + len, + }; + flutter_rust_bridge::for_generated::new_leak_box_ptr(wrap) + } + #[no_mangle] pub extern "C" fn frbgen_breez_liquid_cst_new_list_external_input_parser( len: i32, @@ -13355,7 +13650,25 @@ mod io { #[derive(Clone, Copy)] pub struct wire_cst_asset_balance { asset_id: *mut wire_cst_list_prim_u_8_strict, - balance: u64, + balance_sat: u64, + name: *mut wire_cst_list_prim_u_8_strict, + ticker: *mut wire_cst_list_prim_u_8_strict, + balance: *mut f64, + } + #[repr(C)] + #[derive(Clone, Copy)] + pub struct wire_cst_asset_info { + name: *mut wire_cst_list_prim_u_8_strict, + ticker: *mut wire_cst_list_prim_u_8_strict, + amount: f64, + } + #[repr(C)] + #[derive(Clone, Copy)] + pub struct wire_cst_asset_metadata { + asset_id: *mut wire_cst_list_prim_u_8_strict, + name: *mut wire_cst_list_prim_u_8_strict, + ticker: *mut wire_cst_list_prim_u_8_strict, + precision: u8, } #[repr(C)] #[derive(Clone, Copy)] @@ -13417,6 +13730,7 @@ mod io { external_input_parsers: *mut wire_cst_list_external_input_parser, use_default_external_input_parsers: bool, onchain_fee_rate_leeway_sat_per_vbyte: *mut u32, + asset_metadata: *mut wire_cst_list_asset_metadata, } #[repr(C)] #[derive(Clone, Copy)] @@ -13574,6 +13888,7 @@ mod io { address: *mut wire_cst_list_prim_u_8_strict, network: i32, asset_id: *mut wire_cst_list_prim_u_8_strict, + amount: *mut f64, amount_sat: *mut u64, label: *mut wire_cst_list_prim_u_8_strict, message: *mut wire_cst_list_prim_u_8_strict, @@ -13592,6 +13907,12 @@ mod io { } #[repr(C)] #[derive(Clone, Copy)] + pub struct wire_cst_list_asset_metadata { + ptr: *mut wire_cst_asset_metadata, + len: i32, + } + #[repr(C)] + #[derive(Clone, Copy)] pub struct wire_cst_list_external_input_parser { ptr: *mut wire_cst_external_input_parser, len: i32, @@ -14092,7 +14413,7 @@ mod io { #[derive(Clone, Copy)] pub struct wire_cst_PayAmount_Asset { asset_id: *mut wire_cst_list_prim_u_8_strict, - receiver_amount: u64, + receiver_amount: f64, } #[repr(C)] #[derive(Clone, Copy)] @@ -14149,6 +14470,7 @@ mod io { destination: *mut wire_cst_list_prim_u_8_strict, description: *mut wire_cst_list_prim_u_8_strict, asset_id: *mut wire_cst_list_prim_u_8_strict, + asset_info: *mut wire_cst_asset_info, } #[repr(C)] #[derive(Clone, Copy)] @@ -14171,6 +14493,7 @@ mod io { #[derive(Clone, Copy)] pub union PaymentErrorKind { AmountMissing: wire_cst_PaymentError_AmountMissing, + AssetError: wire_cst_PaymentError_AssetError, InvalidNetwork: wire_cst_PaymentError_InvalidNetwork, Generic: wire_cst_PaymentError_Generic, InvalidDescription: wire_cst_PaymentError_InvalidDescription, @@ -14189,6 +14512,11 @@ mod io { } #[repr(C)] #[derive(Clone, Copy)] + pub struct wire_cst_PaymentError_AssetError { + err: *mut wire_cst_list_prim_u_8_strict, + } + #[repr(C)] + #[derive(Clone, Copy)] pub struct wire_cst_PaymentError_InvalidNetwork { err: *mut wire_cst_list_prim_u_8_strict, } @@ -14346,7 +14674,7 @@ mod io { #[derive(Clone, Copy)] pub struct wire_cst_ReceiveAmount_Asset { asset_id: *mut wire_cst_list_prim_u_8_strict, - payer_amount: *mut u64, + payer_amount: *mut f64, } #[repr(C)] #[derive(Clone, Copy)] diff --git a/lib/core/src/model.rs b/lib/core/src/model.rs index 5d4c8bcee..079f9bdbf 100644 --- a/lib/core/src/model.rs +++ b/lib/core/src/model.rs @@ -72,6 +72,11 @@ pub struct Config { /// /// Defaults to zero. pub onchain_fee_rate_leeway_sat_per_vbyte: Option, + /// A set of asset metadata used by [LiquidSdk::parse](crate::sdk::LiquidSdk::parse) when the input is a + /// [LiquidAddressData] and the [asset_id](LiquidAddressData::asset_id) differs from the Liquid Bitcoin asset. + /// See [AssetMetadata] for more details on how define asset metadata. + /// By default the asset metadata for Liquid Bitcoin and Tether USD are included. + pub asset_metadata: Option>, } impl Config { @@ -91,6 +96,7 @@ impl Config { external_input_parsers: None, use_default_external_input_parsers: true, onchain_fee_rate_leeway_sat_per_vbyte: None, + asset_metadata: None, } } @@ -110,6 +116,7 @@ impl Config { external_input_parsers: None, use_default_external_input_parsers: true, onchain_fee_rate_leeway_sat_per_vbyte: None, + asset_metadata: None, } } @@ -349,7 +356,7 @@ pub enum ReceiveAmount { /// The amount of an asset that should be paid Asset { asset_id: String, - payer_amount: Option, + payer_amount: Option, }, } @@ -492,7 +499,7 @@ pub enum PayAmount { /// The amount of an asset that will be received Asset { asset_id: String, - receiver_amount: u64, + receiver_amount: f64, }, /// Indicates that all available Bitcoin funds should be sent @@ -560,10 +567,13 @@ pub struct RefundResponse { } /// An asset balance to denote the balance for each asset. -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct AssetBalance { pub asset_id: String, - pub balance: u64, + pub balance_sat: u64, + pub name: Option, + pub ticker: Option, + pub balance: Option, } #[derive(Debug, Serialize, Deserialize, Default)] @@ -609,7 +619,7 @@ impl WalletInfo { .find(|ab| ab.asset_id.eq(asset_id)) { Some(asset_balance) => ensure_sdk!( - amount_sat <= asset_balance.balance && fees_sat <= self.balance_sat, + amount_sat <= asset_balance.balance_sat && fees_sat <= self.balance_sat, PaymentError::InsufficientFunds ), None => return Err(PaymentError::InsufficientFunds), @@ -1397,7 +1407,7 @@ pub struct PaymentTxData { pub is_confirmed: bool, /// Data to use in the `blinded` param when unblinding the transaction in an explorer. - /// See: https://docs.liquid.net/docs/unblinding-transactions + /// See: pub unblinding_data: Option, } @@ -1460,6 +1470,45 @@ pub struct LnUrlInfo { pub lnurl_withdraw_endpoint: Option, } +/// Configuration for asset metadata. Each asset metadata item represents an entry in the +/// [Liquid Asset Registry](https://docs.liquid.net/docs/blockstream-liquid-asset-registry). +/// An example Liquid Asset in the registry would be [Tether USD](https://assets.blockstream.info/ce091c998b83c78bb71a632313ba3760f1763d9cfcffae02258ffa9865a37bd2.json>). +#[derive(Debug, Clone, Serialize)] +pub struct AssetMetadata { + /// The asset id of the registered asset + pub asset_id: String, + /// The name of the asset + pub name: String, + /// The ticker of the asset + pub ticker: String, + /// The precision used to display the asset amount. + /// For example, precision of 2 shifts the decimal 2 places left from the satoshi amount. + pub precision: u8, +} + +impl AssetMetadata { + pub fn amount_to_sat(&self, amount: f64) -> u64 { + (amount * (10_u64.pow(self.precision.into()) as f64)) as u64 + } + + pub fn amount_from_sat(&self, amount_sat: u64) -> f64 { + amount_sat as f64 / (10_u64.pow(self.precision.into()) as f64) + } +} + +/// Represents the Liquid payment asset info. The asset info is derived from +/// the available [AssetMetadata] that is set in the [Config]. +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct AssetInfo { + /// The name of the asset + pub name: String, + /// The ticker of the asset + pub ticker: String, + /// The amount calculated from the satoshi amount of the transaction, having its + /// decimal shifted to the left by the [precision](AssetMetadata::precision) + pub amount: f64, +} + /// The specific details of a payment, depending on its type #[derive(Debug, Clone, PartialEq, Serialize)] #[allow(clippy::large_enum_variant)] @@ -1509,6 +1558,9 @@ pub enum PaymentDetails { /// The asset id asset_id: String, + + /// The asset info derived from the [AssetMetadata] + asset_info: Option, }, /// Swapping to or from the Bitcoin chain Bitcoin { @@ -1584,7 +1636,7 @@ pub struct Payment { pub tx_id: Option, /// Data to use in the `blinded` param when unblinding the transaction in an explorer. - /// See: https://docs.liquid.net/docs/unblinding-transactions + /// See: pub unblinding_data: Option, /// Composite timestamp that can be used for sorting or displaying the payment. diff --git a/lib/core/src/persist/asset_metadata.rs b/lib/core/src/persist/asset_metadata.rs new file mode 100644 index 000000000..0dd3a87eb --- /dev/null +++ b/lib/core/src/persist/asset_metadata.rs @@ -0,0 +1,64 @@ +use anyhow::Result; +use rusqlite::{Connection, Row}; + +use super::{AssetMetadata, Persister}; + +impl Persister { + pub(crate) fn replace_asset_metadata( + &self, + asset_metadata: Option>, + ) -> Result<()> { + let con = self.get_connection()?; + con.execute("DELETE FROM asset_metadata WHERE is_default = 0", [])?; + if let Some(asset_metadata) = asset_metadata { + for am in asset_metadata { + con.execute( + "INSERT INTO asset_metadata (asset_id, name, ticker, precision) VALUES (?, ?, ?, ?)", + (am.asset_id, am.name, am.ticker, am.precision), + )?; + } + } + + Ok(()) + } + + pub(crate) fn list_asset_metadata(&self) -> Result> { + let con = self.get_connection()?; + let mut stmt = con.prepare( + "SELECT asset_id, + name, + ticker, + precision + FROM asset_metadata", + )?; + let asset_metadata: Vec = stmt + .query_map([], Self::sql_row_to_asset_metadata)? + .map(|i| i.unwrap()) + .collect(); + Ok(asset_metadata) + } + + pub(crate) fn get_asset_metadata(&self, asset_id: &str) -> Result> { + let con: Connection = self.get_connection()?; + let mut stmt = con.prepare( + "SELECT asset_id, + name, + ticker, + precision + FROM asset_metadata + WHERE asset_id = ?", + )?; + let res = stmt.query_row([asset_id], Self::sql_row_to_asset_metadata); + + Ok(res.ok()) + } + + fn sql_row_to_asset_metadata(row: &Row) -> rusqlite::Result { + Ok(AssetMetadata { + asset_id: row.get(0)?, + name: row.get(1)?, + ticker: row.get(2)?, + precision: row.get(3)?, + }) + } +} diff --git a/lib/core/src/persist/migrations.rs b/lib/core/src/persist/migrations.rs index 4cdbed0e1..3730ed6fe 100644 --- a/lib/core/src/persist/migrations.rs +++ b/lib/core/src/persist/migrations.rs @@ -4,6 +4,21 @@ pub(crate) fn current_migrations(is_mainnet: bool) -> Vec<&'static str> { } else { "ALTER TABLE payment_tx_data ADD COLUMN asset_id TEXT NOT NULL DEFAULT '144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49';" }; + let insert_default_asset_metadata = if is_mainnet { + " + INSERT INTO asset_metadata (asset_id, name, ticker, precision, is_default) + VALUES + ('6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d', 'Bitcoin', 'BTC', 8, 1), + ('ce091c998b83c78bb71a632313ba3760f1763d9cfcffae02258ffa9865a37bd2', 'Tether USD', 'USDt', 8, 1); + " + } else { + " + INSERT INTO asset_metadata (asset_id, name, ticker, precision, is_default) + VALUES + ('144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49', 'Testnet Bitcoin', 'BTC', 8, 1), + ('b612eb46313a2cd6ebabd8b7a8eed5696e29898b87a43bff41c94f51acef9d73', 'Testnet Tether USD', 'USDt', 8, 1); + " + }; vec![ "CREATE TABLE IF NOT EXISTS receive_swaps ( id TEXT NOT NULL PRIMARY KEY, @@ -259,5 +274,16 @@ pub(crate) fn current_migrations(is_mainnet: bool) -> Vec<&'static str> { ALTER TABLE payment_tx_data RENAME COLUMN amount_sat TO amount; UPDATE payment_tx_data SET amount = amount - fees_sat WHERE payment_type = 1; ", + " + DELETE FROM cached_items WHERE key = 'wallet_info'; + CREATE TABLE IF NOT EXISTS asset_metadata( + asset_id TEXT NOT NULL PRIMARY KEY, + name TEXT NOT NULL, + ticker TEXT NOT NULL, + precision INTEGER NOT NULL DEFAULT 8, + is_default INTEGER NOT NULL DEFAULT 0 + ) STRICT; + ", + insert_default_asset_metadata, ] } diff --git a/lib/core/src/persist/mod.rs b/lib/core/src/persist/mod.rs index f6cff0b64..629ffab5d 100644 --- a/lib/core/src/persist/mod.rs +++ b/lib/core/src/persist/mod.rs @@ -1,4 +1,5 @@ mod address; +pub(crate) mod asset_metadata; mod backup; pub(crate) mod cache; pub(crate) mod chain; @@ -435,7 +436,10 @@ impl Persister { rtx.amount, pd.destination, pd.description, - pd.lnurl_info_json + pd.lnurl_info_json, + am.name, + am.ticker, + am.precision FROM payment_tx_data AS ptx -- Payment tx (each tx results in a Payment) FULL JOIN ( SELECT * FROM receive_swaps @@ -457,6 +461,8 @@ impl Persister { ON rtx.tx_id in (ss.refund_tx_id, cs.refund_tx_id) LEFT JOIN payment_details AS pd -- Payment details ON pd.tx_id = ptx.tx_id + LEFT JOIN asset_metadata AS am -- Asset metadata + ON am.asset_id = ptx.asset_id WHERE (ptx.tx_id IS NULL -- Filter out refund txs from Chain/Send Swaps OR ptx.tx_id NOT IN (SELECT refund_tx_id FROM send_swaps WHERE refund_tx_id NOT NULL) @@ -551,6 +557,10 @@ impl Persister { let maybe_payment_details_lnurl_info: Option = maybe_payment_details_lnurl_info_json.and_then(|info| serde_json::from_str(&info).ok()); + let maybe_asset_metadata_name: Option = row.get(53)?; + let maybe_asset_metadata_ticker: Option = row.get(54)?; + let maybe_asset_metadata_precision: Option = row.get(55)?; + let (swap, payment_type) = match maybe_receive_swap_id { Some(receive_swap_id) => { let payer_amount_sat = maybe_receive_swap_payer_amount_sat.unwrap_or(0); @@ -740,17 +750,43 @@ impl Persister { auto_accepted_fees, } } - _ => PaymentDetails::Liquid { - asset_id: tx + _ => { + let (amount, asset_id) = tx .clone() - .map_or(utils::lbtc_asset_id(self.network).to_string(), |ptd| { - ptd.asset_id - }), - destination: maybe_payment_details_destination - .unwrap_or("Destination unknown".to_string()), - description: maybe_payment_details_description - .unwrap_or("Liquid transfer".to_string()), - }, + .map_or((0, utils::lbtc_asset_id(self.network).to_string()), |ptd| { + (ptd.amount, ptd.asset_id) + }); + let asset_info = match ( + maybe_asset_metadata_name, + maybe_asset_metadata_ticker, + maybe_asset_metadata_precision, + ) { + (Some(name), Some(ticker), Some(precision)) => { + let asset_metadata = AssetMetadata { + asset_id: asset_id.clone(), + name: name.clone(), + ticker: ticker.clone(), + precision, + }; + + Some(AssetInfo { + name, + ticker, + amount: asset_metadata.amount_from_sat(amount), + }) + } + _ => None, + }; + + PaymentDetails::Liquid { + destination: maybe_payment_details_destination + .unwrap_or("Destination unknown".to_string()), + description: maybe_payment_details_description + .unwrap_or("Liquid transfer".to_string()), + asset_id, + asset_info, + } + } }; match (tx, swap.clone()) { diff --git a/lib/core/src/sdk.rs b/lib/core/src/sdk.rs index c691a1f37..a6dd9b68b 100644 --- a/lib/core/src/sdk.rs +++ b/lib/core/src/sdk.rs @@ -174,6 +174,7 @@ impl LiquidSdk { let persister = Arc::new(Persister::new(&working_dir, config.network, None)?); persister.init()?; + persister.replace_asset_metadata(config.asset_metadata.clone())?; let liquid_chain_service = Arc::new(Mutex::new(HybridLiquidChainService::new(config.clone())?)); @@ -928,16 +929,12 @@ impl LiquidSdk { address: mut liquid_address_data, }) => { let amount = match ( + liquid_address_data.amount, liquid_address_data.amount_sat, liquid_address_data.asset_id, req.amount.clone(), ) { - (None, _, None) => { - return Err(PaymentError::AmountMissing { - err: "Amount must be set when paying to a Liquid address".to_string(), - }); - } - (Some(amount_sat), Some(asset_id), None) => { + (Some(amount), Some(amount_sat), Some(asset_id), None) => { if asset_id.eq(&self.config.lbtc_asset_id()) { PayAmount::Bitcoin { receiver_amount_sat: amount_sat, @@ -945,14 +942,19 @@ impl LiquidSdk { } else { PayAmount::Asset { asset_id, - receiver_amount: amount_sat, + receiver_amount: amount, } } } - (Some(amount_sat), None, None) => PayAmount::Bitcoin { + (_, Some(amount_sat), None, None) => PayAmount::Bitcoin { receiver_amount_sat: amount_sat, }, - (_, _, Some(amount)) => amount, + (_, _, _, Some(amount)) => amount, + _ => { + return Err(PaymentError::AmountMissing { + err: "Amount must be set when paying to a Liquid address".to_string(), + }); + } }; ensure_sdk!( @@ -1004,14 +1006,20 @@ impl LiquidSdk { asset_id, receiver_amount, } => { + let asset_metadata = self.persister.get_asset_metadata(&asset_id)?.ok_or( + PaymentError::AssetError { + err: format!("Asset {asset_id} is not supported"), + }, + )?; + let receiver_amount_sat = asset_metadata.amount_to_sat(receiver_amount); let fees_sat = self .estimate_onchain_tx_or_drain_tx_fee( - receiver_amount, + receiver_amount_sat, &liquid_address_data.address, &asset_id, ) .await?; - (asset_id, receiver_amount, fees_sat) + (asset_id, receiver_amount_sat, fees_sat) } }; @@ -1160,10 +1168,14 @@ impl LiquidSdk { address_data: liquid_address_data, } => { let Some(amount_sat) = liquid_address_data.amount_sat else { - return Err(PaymentError::AmountMissing { err: "`amount_sat` must be present when paying to a `SendDestination::LiquidAddress`".to_string() }); + return Err(PaymentError::AmountMissing { + err: "Amount must be set when paying to a Liquid address".to_string(), + }); }; let Some(ref asset_id) = liquid_address_data.asset_id else { - return Err(PaymentError::Generic { err: "`asset_id` must be present when paying to a `SendDestination::LiquidAddress`".to_string() }); + return Err(PaymentError::asset_error( + "Asset must be set when paying to a Liquid address", + )); }; ensure_sdk!( @@ -1234,6 +1246,7 @@ impl LiquidSdk { address, network: self.config.network.into(), asset_id: None, + amount: None, amount_sat: None, label: None, message: None, @@ -1359,10 +1372,19 @@ impl LiquidSdk { )?; self.emit_payment_updated(Some(tx_id)).await?; // Emit Pending event + let asset_info = self + .persister + .get_asset_metadata(&asset_id)? + .map(|ref am| AssetInfo { + name: am.name.clone(), + ticker: am.ticker.clone(), + amount: am.amount_from_sat(receiver_amount_sat), + }); let payment_details = PaymentDetails::Liquid { asset_id, destination, description: description.unwrap_or("Liquid transfer".to_string()), + asset_info, }; Ok(SendPaymentResponse { @@ -1621,7 +1643,7 @@ impl LiquidSdk { (payer_amount_sat, receiver_amount_sat, total_fees_sat) } PayAmount::Asset { .. } => { - return Err(PaymentError::generic( + return Err(PaymentError::asset_error( "Cannot send an asset to a Bitcoin address", )) } @@ -1864,7 +1886,7 @@ impl LiquidSdk { PaymentMethod::Lightning => { let payer_amount_sat = match req.amount { Some(ReceiveAmount::Asset { .. }) => { - return Err(PaymentError::generic( + return Err(PaymentError::asset_error( "Cannot receive an asset when the payment method is Lightning", )); } @@ -1900,7 +1922,7 @@ impl LiquidSdk { PaymentMethod::BitcoinAddress => { let payer_amount_sat = match req.amount { Some(ReceiveAmount::Asset { .. }) => { - return Err(PaymentError::generic( + return Err(PaymentError::asset_error( "Cannot receive an asset when the payment method is Bitcoin", )); } @@ -1923,18 +1945,18 @@ impl LiquidSdk { debug!("Preparing Chain Receive Swap with: payer_amount_sat {payer_amount_sat:?}, fees_sat {fees_sat}"); } PaymentMethod::LiquidAddress => { - let (asset_id, amount_sat) = match req.amount.clone() { + let (asset_id, payer_amount, payer_amount_sat) = match req.amount.clone() { Some(ReceiveAmount::Asset { payer_amount, asset_id, - }) => (asset_id, payer_amount), + }) => (asset_id, payer_amount, None), Some(ReceiveAmount::Bitcoin { payer_amount_sat }) => { - (self.config.lbtc_asset_id(), Some(payer_amount_sat)) + (self.config.lbtc_asset_id(), None, Some(payer_amount_sat)) } - None => (self.config.lbtc_asset_id(), None), + None => (self.config.lbtc_asset_id(), None, None), }; fees_sat = 0; - debug!("Preparing Liquid Receive with: asset_id {asset_id}, amount_sat {amount_sat:?}, fees_sat {fees_sat}"); + debug!("Preparing Liquid Receive with: asset_id {asset_id}, amount {payer_amount:?}, amount_sat {payer_amount_sat:?}, fees_sat {fees_sat}"); } }; @@ -1979,7 +2001,7 @@ impl LiquidSdk { PaymentMethod::Lightning => { let amount_sat = match amount.clone() { Some(ReceiveAmount::Asset { .. }) => { - return Err(PaymentError::generic( + return Err(PaymentError::asset_error( "Cannot receive an asset when the payment method is Lightning", )); } @@ -2011,7 +2033,7 @@ impl LiquidSdk { PaymentMethod::BitcoinAddress => { let amount_sat = match amount.clone() { Some(ReceiveAmount::Asset { .. }) => { - return Err(PaymentError::generic( + return Err(PaymentError::asset_error( "Cannot receive an asset when the payment method is Bitcoin", )); } @@ -2021,33 +2043,37 @@ impl LiquidSdk { self.receive_onchain(amount_sat, *fees_sat).await } PaymentMethod::LiquidAddress => { - let (asset_id, amount_sat) = match amount.clone() { + let lbtc_asset_id = self.config.lbtc_asset_id(); + let (asset_id, amount, amount_sat) = match amount.clone() { Some(ReceiveAmount::Asset { asset_id, payer_amount, - }) => (asset_id, payer_amount), + }) => (asset_id, payer_amount, None), Some(ReceiveAmount::Bitcoin { payer_amount_sat }) => { - (self.config.lbtc_asset_id(), Some(payer_amount_sat)) + (lbtc_asset_id.clone(), None, Some(payer_amount_sat)) } - None => (self.config.lbtc_asset_id(), None), + None => (lbtc_asset_id.clone(), None, None), }; let address = self.onchain_wallet.next_unused_address().await?.to_string(); - let receive_destination = match amount_sat { - Some(amount_sat) => LiquidAddressData { - address: address.to_string(), - network: self.config.network.into(), - amount_sat: Some(amount_sat), - asset_id: Some(asset_id.clone()), - label: None, - message: req.description.clone(), - } - .to_uri() - .map_err(|e| PaymentError::Generic { - err: format!("Could not build BIP21 URI: {e:?}"), - })?, - None => address, - }; + let receive_destination = + if asset_id.ne(&lbtc_asset_id) || amount.is_some() || amount_sat.is_some() { + LiquidAddressData { + address: address.to_string(), + network: self.config.network.into(), + amount, + amount_sat, + asset_id: Some(asset_id), + label: None, + message: req.description.clone(), + } + .to_uri() + .map_err(|e| PaymentError::Generic { + err: format!("Could not build BIP21 URI: {e:?}"), + })? + } else { + address + }; Ok(ReceivePaymentResponse { destination: receive_destination, @@ -2694,6 +2720,12 @@ impl LiquidSdk { } async fn update_wallet_info(&self) -> Result<()> { + let asset_metadata: HashMap = self + .persister + .list_asset_metadata()? + .into_iter() + .map(|am| (am.asset_id.clone(), am)) + .collect(); let transactions = self.onchain_wallet.transactions().await?; let asset_balances = transactions .into_iter() @@ -2707,16 +2739,24 @@ impl LiquidSdk { acc }) .into_iter() - .map(|(asset_id, balance)| AssetBalance { - asset_id: asset_id.to_hex(), - balance: balance.unsigned_abs(), + .map(|(asset_id, balance)| { + let asset_id = asset_id.to_hex(); + let balance_sat = balance.unsigned_abs(); + let maybe_asset_metadata = asset_metadata.get(&asset_id); + AssetBalance { + asset_id, + balance_sat, + name: maybe_asset_metadata.map(|am| am.name.clone()), + ticker: maybe_asset_metadata.map(|am| am.ticker.clone()), + balance: maybe_asset_metadata.map(|am| am.amount_from_sat(balance_sat)), + } }) .collect::>(); let balance_sat = asset_balances .clone() .into_iter() .find(|ab| ab.asset_id.eq(&self.config.lbtc_asset_id())) - .map_or(0, |ab| ab.balance); + .map_or(0, |ab| ab.balance_sat); debug!("Onchain wallet balance: {balance_sat} sats"); let mut pending_send_sat = 0; @@ -3261,9 +3301,27 @@ impl LiquidSdk { /// Can optionally be configured to use external input parsers by providing `external_input_parsers` in [Config]. pub async fn parse(&self, input: &str) -> Result { let external_parsers = &self.external_input_parsers; - parse(input, Some(external_parsers)) + let input_type = parse(input, Some(external_parsers)) .await - .map_err(|e| PaymentError::generic(&e.to_string())) + .map_err(|e| PaymentError::generic(&e.to_string()))?; + + let res = match input_type { + InputType::LiquidAddress { ref address } => match &address.asset_id { + Some(asset_id) if asset_id.ne(&self.config.lbtc_asset_id()) => { + let asset_metadata = self.persister.get_asset_metadata(asset_id)?.ok_or( + PaymentError::AssetError { + err: format!("Asset {asset_id} is not supported"), + }, + )?; + let mut address = address.clone(); + address.set_amount_precision(asset_metadata.precision.into()); + InputType::LiquidAddress { address } + } + _ => input_type, + }, + _ => input_type, + }; + Ok(res) } /// Parses a string into an [LNInvoice]. See [invoice::parse_invoice]. diff --git a/packages/dart/lib/src/bindings.dart b/packages/dart/lib/src/bindings.dart index 23aebdf84..b2f15978e 100644 --- a/packages/dart/lib/src/bindings.dart +++ b/packages/dart/lib/src/bindings.dart @@ -350,6 +350,7 @@ class LiquidAddressData { final String address; final Network network; final String? assetId; + final double? amount; final BigInt? amountSat; final String? label; final String? message; @@ -358,6 +359,7 @@ class LiquidAddressData { required this.address, required this.network, this.assetId, + this.amount, this.amountSat, this.label, this.message, @@ -368,6 +370,7 @@ class LiquidAddressData { address.hashCode ^ network.hashCode ^ assetId.hashCode ^ + amount.hashCode ^ amountSat.hashCode ^ label.hashCode ^ message.hashCode; @@ -380,6 +383,7 @@ class LiquidAddressData { address == other.address && network == other.network && assetId == other.assetId && + amount == other.amount && amountSat == other.amountSat && label == other.label && message == other.message; diff --git a/packages/dart/lib/src/error.dart b/packages/dart/lib/src/error.dart index ab6aaaa66..5b20911af 100644 --- a/packages/dart/lib/src/error.dart +++ b/packages/dart/lib/src/error.dart @@ -19,6 +19,9 @@ sealed class PaymentError with _$PaymentError implements FrbException { const factory PaymentError.amountMissing({ required String err, }) = PaymentError_AmountMissing; + const factory PaymentError.assetError({ + required String err, + }) = PaymentError_AssetError; const factory PaymentError.invalidNetwork({ required String err, }) = PaymentError_InvalidNetwork; diff --git a/packages/dart/lib/src/error.freezed.dart b/packages/dart/lib/src/error.freezed.dart index 5efacc7b2..c6dc6ff8d 100644 --- a/packages/dart/lib/src/error.freezed.dart +++ b/packages/dart/lib/src/error.freezed.dart @@ -291,6 +291,85 @@ abstract class PaymentError_AmountMissing extends PaymentError { throw _privateConstructorUsedError; } +/// @nodoc +abstract class _$$PaymentError_AssetErrorImplCopyWith<$Res> { + factory _$$PaymentError_AssetErrorImplCopyWith( + _$PaymentError_AssetErrorImpl value, $Res Function(_$PaymentError_AssetErrorImpl) then) = + __$$PaymentError_AssetErrorImplCopyWithImpl<$Res>; + @useResult + $Res call({String err}); +} + +/// @nodoc +class __$$PaymentError_AssetErrorImplCopyWithImpl<$Res> + extends _$PaymentErrorCopyWithImpl<$Res, _$PaymentError_AssetErrorImpl> + implements _$$PaymentError_AssetErrorImplCopyWith<$Res> { + __$$PaymentError_AssetErrorImplCopyWithImpl( + _$PaymentError_AssetErrorImpl _value, $Res Function(_$PaymentError_AssetErrorImpl) _then) + : super(_value, _then); + + /// Create a copy of PaymentError + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? err = null, + }) { + return _then(_$PaymentError_AssetErrorImpl( + err: null == err + ? _value.err + : err // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc + +class _$PaymentError_AssetErrorImpl extends PaymentError_AssetError { + const _$PaymentError_AssetErrorImpl({required this.err}) : super._(); + + @override + final String err; + + @override + String toString() { + return 'PaymentError.assetError(err: $err)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$PaymentError_AssetErrorImpl && + (identical(other.err, err) || other.err == err)); + } + + @override + int get hashCode => Object.hash(runtimeType, err); + + /// Create a copy of PaymentError + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$PaymentError_AssetErrorImplCopyWith<_$PaymentError_AssetErrorImpl> get copyWith => + __$$PaymentError_AssetErrorImplCopyWithImpl<_$PaymentError_AssetErrorImpl>(this, _$identity); +} + +abstract class PaymentError_AssetError extends PaymentError { + const factory PaymentError_AssetError({required final String err}) = _$PaymentError_AssetErrorImpl; + const PaymentError_AssetError._() : super._(); + + String get err; + + /// Create a copy of PaymentError + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + _$$PaymentError_AssetErrorImplCopyWith<_$PaymentError_AssetErrorImpl> get copyWith => + throw _privateConstructorUsedError; +} + /// @nodoc abstract class _$$PaymentError_InvalidNetworkImplCopyWith<$Res> { factory _$$PaymentError_InvalidNetworkImplCopyWith( diff --git a/packages/dart/lib/src/frb_generated.dart b/packages/dart/lib/src/frb_generated.dart index caf7d8052..f9f0d67d7 100644 --- a/packages/dart/lib/src/frb_generated.dart +++ b/packages/dart/lib/src/frb_generated.dart @@ -1382,10 +1382,38 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { AssetBalance dco_decode_asset_balance(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 2) throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); + if (arr.length != 5) throw Exception('unexpected arr length: expect 5 but see ${arr.length}'); return AssetBalance( assetId: dco_decode_String(arr[0]), - balance: dco_decode_u_64(arr[1]), + balanceSat: dco_decode_u_64(arr[1]), + name: dco_decode_opt_String(arr[2]), + ticker: dco_decode_opt_String(arr[3]), + balance: dco_decode_opt_box_autoadd_f_64(arr[4]), + ); + } + + @protected + AssetInfo dco_decode_asset_info(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + final arr = raw as List; + if (arr.length != 3) throw Exception('unexpected arr length: expect 3 but see ${arr.length}'); + return AssetInfo( + name: dco_decode_String(arr[0]), + ticker: dco_decode_String(arr[1]), + amount: dco_decode_f_64(arr[2]), + ); + } + + @protected + AssetMetadata dco_decode_asset_metadata(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + final arr = raw as List; + if (arr.length != 4) throw Exception('unexpected arr length: expect 4 but see ${arr.length}'); + return AssetMetadata( + assetId: dco_decode_String(arr[0]), + name: dco_decode_String(arr[1]), + ticker: dco_decode_String(arr[2]), + precision: dco_decode_u_8(arr[3]), ); } @@ -1470,6 +1498,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return dco_decode_amount(raw); } + @protected + AssetInfo dco_decode_box_autoadd_asset_info(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return dco_decode_asset_info(raw); + } + @protected BackupRequest dco_decode_box_autoadd_backup_request(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -1807,7 +1841,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { Config dco_decode_config(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 14) throw Exception('unexpected arr length: expect 14 but see ${arr.length}'); + if (arr.length != 15) throw Exception('unexpected arr length: expect 15 but see ${arr.length}'); return Config( liquidElectrumUrl: dco_decode_String(arr[0]), bitcoinElectrumUrl: dco_decode_String(arr[1]), @@ -1823,6 +1857,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { externalInputParsers: dco_decode_opt_list_external_input_parser(arr[11]), useDefaultExternalInputParsers: dco_decode_bool(arr[12]), onchainFeeRateLeewaySatPerVbyte: dco_decode_opt_box_autoadd_u_32(arr[13]), + assetMetadata: dco_decode_opt_list_asset_metadata(arr[14]), ); } @@ -2017,14 +2052,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { LiquidAddressData dco_decode_liquid_address_data(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 6) throw Exception('unexpected arr length: expect 6 but see ${arr.length}'); + if (arr.length != 7) throw Exception('unexpected arr length: expect 7 but see ${arr.length}'); return LiquidAddressData( address: dco_decode_String(arr[0]), network: dco_decode_network(arr[1]), assetId: dco_decode_opt_String(arr[2]), - amountSat: dco_decode_opt_box_autoadd_u_64(arr[3]), - label: dco_decode_opt_String(arr[4]), - message: dco_decode_opt_String(arr[5]), + amount: dco_decode_opt_box_autoadd_f_64(arr[3]), + amountSat: dco_decode_opt_box_autoadd_u_64(arr[4]), + label: dco_decode_opt_String(arr[5]), + message: dco_decode_opt_String(arr[6]), ); } @@ -2046,6 +2082,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return (raw as List).map(dco_decode_asset_balance).toList(); } + @protected + List dco_decode_list_asset_metadata(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return (raw as List).map(dco_decode_asset_metadata).toList(); + } + @protected List dco_decode_list_external_input_parser(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -2571,6 +2613,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return raw == null ? null : dco_decode_box_autoadd_amount(raw); } + @protected + AssetInfo? dco_decode_opt_box_autoadd_asset_info(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return raw == null ? null : dco_decode_box_autoadd_asset_info(raw); + } + @protected bool? dco_decode_opt_box_autoadd_bool(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -2649,6 +2697,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return raw == null ? null : dco_decode_box_autoadd_u_64(raw); } + @protected + List? dco_decode_opt_list_asset_metadata(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return raw == null ? null : dco_decode_list_asset_metadata(raw); + } + @protected List? dco_decode_opt_list_external_input_parser(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -2678,7 +2732,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { case 1: return PayAmount_Asset( assetId: dco_decode_String(raw[1]), - receiverAmount: dco_decode_u_64(raw[2]), + receiverAmount: dco_decode_f_64(raw[2]), ); case 2: return PayAmount_Drain(); @@ -2740,6 +2794,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { destination: dco_decode_String(raw[1]), description: dco_decode_String(raw[2]), assetId: dco_decode_String(raw[3]), + assetInfo: dco_decode_opt_box_autoadd_asset_info(raw[4]), ); case 2: return PaymentDetails_Bitcoin( @@ -2773,53 +2828,57 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { err: dco_decode_String(raw[1]), ); case 5: - return PaymentError_InvalidNetwork( + return PaymentError_AssetError( err: dco_decode_String(raw[1]), ); case 6: - return PaymentError_Generic( + return PaymentError_InvalidNetwork( err: dco_decode_String(raw[1]), ); case 7: - return PaymentError_InvalidOrExpiredFees(); + return PaymentError_Generic( + err: dco_decode_String(raw[1]), + ); case 8: - return PaymentError_InsufficientFunds(); + return PaymentError_InvalidOrExpiredFees(); case 9: + return PaymentError_InsufficientFunds(); + case 10: return PaymentError_InvalidDescription( err: dco_decode_String(raw[1]), ); - case 10: + case 11: return PaymentError_InvalidInvoice( err: dco_decode_String(raw[1]), ); - case 11: - return PaymentError_InvalidPreimage(); case 12: + return PaymentError_InvalidPreimage(); + case 13: return PaymentError_LwkError( err: dco_decode_String(raw[1]), ); - case 13: - return PaymentError_PairsNotFound(); case 14: - return PaymentError_PaymentTimeout(); + return PaymentError_PairsNotFound(); case 15: - return PaymentError_PersistError(); + return PaymentError_PaymentTimeout(); case 16: + return PaymentError_PersistError(); + case 17: return PaymentError_ReceiveError( err: dco_decode_String(raw[1]), ); - case 17: + case 18: return PaymentError_Refunded( err: dco_decode_String(raw[1]), refundTxId: dco_decode_String(raw[2]), ); - case 18: - return PaymentError_SelfTransferNotSupported(); case 19: + return PaymentError_SelfTransferNotSupported(); + case 20: return PaymentError_SendError( err: dco_decode_String(raw[1]), ); - case 20: + case 21: return PaymentError_SignerError( err: dco_decode_String(raw[1]), ); @@ -3013,7 +3072,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { case 1: return ReceiveAmount_Asset( assetId: dco_decode_String(raw[1]), - payerAmount: dco_decode_opt_box_autoadd_u_64(raw[2]), + payerAmount: dco_decode_opt_box_autoadd_f_64(raw[2]), ); default: throw Exception("unreachable"); @@ -3487,8 +3546,35 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { AssetBalance sse_decode_asset_balance(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs var var_assetId = sse_decode_String(deserializer); - var var_balance = sse_decode_u_64(deserializer); - return AssetBalance(assetId: var_assetId, balance: var_balance); + var var_balanceSat = sse_decode_u_64(deserializer); + var var_name = sse_decode_opt_String(deserializer); + var var_ticker = sse_decode_opt_String(deserializer); + var var_balance = sse_decode_opt_box_autoadd_f_64(deserializer); + return AssetBalance( + assetId: var_assetId, + balanceSat: var_balanceSat, + name: var_name, + ticker: var_ticker, + balance: var_balance); + } + + @protected + AssetInfo sse_decode_asset_info(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + var var_name = sse_decode_String(deserializer); + var var_ticker = sse_decode_String(deserializer); + var var_amount = sse_decode_f_64(deserializer); + return AssetInfo(name: var_name, ticker: var_ticker, amount: var_amount); + } + + @protected + AssetMetadata sse_decode_asset_metadata(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + var var_assetId = sse_decode_String(deserializer); + var var_name = sse_decode_String(deserializer); + var var_ticker = sse_decode_String(deserializer); + var var_precision = sse_decode_u_8(deserializer); + return AssetMetadata(assetId: var_assetId, name: var_name, ticker: var_ticker, precision: var_precision); } @protected @@ -3568,6 +3654,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return (sse_decode_amount(deserializer)); } + @protected + AssetInfo sse_decode_box_autoadd_asset_info(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + return (sse_decode_asset_info(deserializer)); + } + @protected BackupRequest sse_decode_box_autoadd_backup_request(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -3911,6 +4003,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { var var_externalInputParsers = sse_decode_opt_list_external_input_parser(deserializer); var var_useDefaultExternalInputParsers = sse_decode_bool(deserializer); var var_onchainFeeRateLeewaySatPerVbyte = sse_decode_opt_box_autoadd_u_32(deserializer); + var var_assetMetadata = sse_decode_opt_list_asset_metadata(deserializer); return Config( liquidElectrumUrl: var_liquidElectrumUrl, bitcoinElectrumUrl: var_bitcoinElectrumUrl, @@ -3925,7 +4018,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { breezApiKey: var_breezApiKey, externalInputParsers: var_externalInputParsers, useDefaultExternalInputParsers: var_useDefaultExternalInputParsers, - onchainFeeRateLeewaySatPerVbyte: var_onchainFeeRateLeewaySatPerVbyte); + onchainFeeRateLeewaySatPerVbyte: var_onchainFeeRateLeewaySatPerVbyte, + assetMetadata: var_assetMetadata); } @protected @@ -4101,6 +4195,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { var var_address = sse_decode_String(deserializer); var var_network = sse_decode_network(deserializer); var var_assetId = sse_decode_opt_String(deserializer); + var var_amount = sse_decode_opt_box_autoadd_f_64(deserializer); var var_amountSat = sse_decode_opt_box_autoadd_u_64(deserializer); var var_label = sse_decode_opt_String(deserializer); var var_message = sse_decode_opt_String(deserializer); @@ -4108,6 +4203,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { address: var_address, network: var_network, assetId: var_assetId, + amount: var_amount, amountSat: var_amountSat, label: var_label, message: var_message); @@ -4144,6 +4240,18 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return ans_; } + @protected + List sse_decode_list_asset_metadata(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + + var len_ = sse_decode_i_32(deserializer); + var ans_ = []; + for (var idx_ = 0; idx_ < len_; ++idx_) { + ans_.add(sse_decode_asset_metadata(deserializer)); + } + return ans_; + } + @protected List sse_decode_list_external_input_parser(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -4730,6 +4838,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } } + @protected + AssetInfo? sse_decode_opt_box_autoadd_asset_info(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + + if (sse_decode_bool(deserializer)) { + return (sse_decode_box_autoadd_asset_info(deserializer)); + } else { + return null; + } + } + @protected bool? sse_decode_opt_box_autoadd_bool(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -4873,6 +4992,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } } + @protected + List? sse_decode_opt_list_asset_metadata(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + + if (sse_decode_bool(deserializer)) { + return (sse_decode_list_asset_metadata(deserializer)); + } else { + return null; + } + } + @protected List? sse_decode_opt_list_external_input_parser(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -4917,7 +5047,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return PayAmount_Bitcoin(receiverAmountSat: var_receiverAmountSat); case 1: var var_assetId = sse_decode_String(deserializer); - var var_receiverAmount = sse_decode_u_64(deserializer); + var var_receiverAmount = sse_decode_f_64(deserializer); return PayAmount_Asset(assetId: var_assetId, receiverAmount: var_receiverAmount); case 2: return PayAmount_Drain(); @@ -4994,8 +5124,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { var var_destination = sse_decode_String(deserializer); var var_description = sse_decode_String(deserializer); var var_assetId = sse_decode_String(deserializer); + var var_assetInfo = sse_decode_opt_box_autoadd_asset_info(deserializer); return PaymentDetails_Liquid( - destination: var_destination, description: var_description, assetId: var_assetId); + destination: var_destination, + description: var_description, + assetId: var_assetId, + assetInfo: var_assetInfo); case 2: var var_swapId = sse_decode_String(deserializer); var var_description = sse_decode_String(deserializer); @@ -5036,44 +5170,47 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return PaymentError_AmountMissing(err: var_err); case 5: var var_err = sse_decode_String(deserializer); - return PaymentError_InvalidNetwork(err: var_err); + return PaymentError_AssetError(err: var_err); case 6: var var_err = sse_decode_String(deserializer); - return PaymentError_Generic(err: var_err); + return PaymentError_InvalidNetwork(err: var_err); case 7: - return PaymentError_InvalidOrExpiredFees(); + var var_err = sse_decode_String(deserializer); + return PaymentError_Generic(err: var_err); case 8: - return PaymentError_InsufficientFunds(); + return PaymentError_InvalidOrExpiredFees(); case 9: + return PaymentError_InsufficientFunds(); + case 10: var var_err = sse_decode_String(deserializer); return PaymentError_InvalidDescription(err: var_err); - case 10: + case 11: var var_err = sse_decode_String(deserializer); return PaymentError_InvalidInvoice(err: var_err); - case 11: - return PaymentError_InvalidPreimage(); case 12: + return PaymentError_InvalidPreimage(); + case 13: var var_err = sse_decode_String(deserializer); return PaymentError_LwkError(err: var_err); - case 13: - return PaymentError_PairsNotFound(); case 14: - return PaymentError_PaymentTimeout(); + return PaymentError_PairsNotFound(); case 15: - return PaymentError_PersistError(); + return PaymentError_PaymentTimeout(); case 16: + return PaymentError_PersistError(); + case 17: var var_err = sse_decode_String(deserializer); return PaymentError_ReceiveError(err: var_err); - case 17: + case 18: var var_err = sse_decode_String(deserializer); var var_refundTxId = sse_decode_String(deserializer); return PaymentError_Refunded(err: var_err, refundTxId: var_refundTxId); - case 18: - return PaymentError_SelfTransferNotSupported(); case 19: + return PaymentError_SelfTransferNotSupported(); + case 20: var var_err = sse_decode_String(deserializer); return PaymentError_SendError(err: var_err); - case 20: + case 21: var var_err = sse_decode_String(deserializer); return PaymentError_SignerError(err: var_err); default: @@ -5252,7 +5389,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return ReceiveAmount_Bitcoin(payerAmountSat: var_payerAmountSat); case 1: var var_assetId = sse_decode_String(deserializer); - var var_payerAmount = sse_decode_opt_box_autoadd_u_64(deserializer); + var var_payerAmount = sse_decode_opt_box_autoadd_f_64(deserializer); return ReceiveAmount_Asset(assetId: var_assetId, payerAmount: var_payerAmount); default: throw UnimplementedError(''); @@ -5794,7 +5931,27 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { void sse_encode_asset_balance(AssetBalance self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_String(self.assetId, serializer); - sse_encode_u_64(self.balance, serializer); + sse_encode_u_64(self.balanceSat, serializer); + sse_encode_opt_String(self.name, serializer); + sse_encode_opt_String(self.ticker, serializer); + sse_encode_opt_box_autoadd_f_64(self.balance, serializer); + } + + @protected + void sse_encode_asset_info(AssetInfo self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_String(self.name, serializer); + sse_encode_String(self.ticker, serializer); + sse_encode_f_64(self.amount, serializer); + } + + @protected + void sse_encode_asset_metadata(AssetMetadata self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_String(self.assetId, serializer); + sse_encode_String(self.name, serializer); + sse_encode_String(self.ticker, serializer); + sse_encode_u_8(self.precision, serializer); } @protected @@ -5865,6 +6022,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_amount(self, serializer); } + @protected + void sse_encode_box_autoadd_asset_info(AssetInfo self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_asset_info(self, serializer); + } + @protected void sse_encode_box_autoadd_backup_request(BackupRequest self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -6211,6 +6374,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_opt_list_external_input_parser(self.externalInputParsers, serializer); sse_encode_bool(self.useDefaultExternalInputParsers, serializer); sse_encode_opt_box_autoadd_u_32(self.onchainFeeRateLeewaySatPerVbyte, serializer); + sse_encode_opt_list_asset_metadata(self.assetMetadata, serializer); } @protected @@ -6358,6 +6522,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_String(self.address, serializer); sse_encode_network(self.network, serializer); sse_encode_opt_String(self.assetId, serializer); + sse_encode_opt_box_autoadd_f_64(self.amount, serializer); sse_encode_opt_box_autoadd_u_64(self.amountSat, serializer); sse_encode_opt_String(self.label, serializer); sse_encode_opt_String(self.message, serializer); @@ -6387,6 +6552,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } } + @protected + void sse_encode_list_asset_metadata(List self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_i_32(self.length, serializer); + for (final item in self) { + sse_encode_asset_metadata(item, serializer); + } + } + @protected void sse_encode_list_external_input_parser(List self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -6839,6 +7013,16 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } } + @protected + void sse_encode_opt_box_autoadd_asset_info(AssetInfo? self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + + sse_encode_bool(self != null, serializer); + if (self != null) { + sse_encode_box_autoadd_asset_info(self, serializer); + } + } + @protected void sse_encode_opt_box_autoadd_bool(bool? self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -6970,6 +7154,16 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } } + @protected + void sse_encode_opt_list_asset_metadata(List? self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + + sse_encode_bool(self != null, serializer); + if (self != null) { + sse_encode_list_asset_metadata(self, serializer); + } + } + @protected void sse_encode_opt_list_external_input_parser(List? self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -7010,7 +7204,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { case PayAmount_Asset(assetId: final assetId, receiverAmount: final receiverAmount): sse_encode_i_32(1, serializer); sse_encode_String(assetId, serializer); - sse_encode_u_64(receiverAmount, serializer); + sse_encode_f_64(receiverAmount, serializer); case PayAmount_Drain(): sse_encode_i_32(2, serializer); } @@ -7070,12 +7264,14 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { case PaymentDetails_Liquid( destination: final destination, description: final description, - assetId: final assetId + assetId: final assetId, + assetInfo: final assetInfo ): sse_encode_i_32(1, serializer); sse_encode_String(destination, serializer); sse_encode_String(description, serializer); sse_encode_String(assetId, serializer); + sse_encode_opt_box_autoadd_asset_info(assetInfo, serializer); case PaymentDetails_Bitcoin( swapId: final swapId, description: final description, @@ -7111,47 +7307,50 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { case PaymentError_AmountMissing(err: final err): sse_encode_i_32(4, serializer); sse_encode_String(err, serializer); - case PaymentError_InvalidNetwork(err: final err): + case PaymentError_AssetError(err: final err): sse_encode_i_32(5, serializer); sse_encode_String(err, serializer); - case PaymentError_Generic(err: final err): + case PaymentError_InvalidNetwork(err: final err): sse_encode_i_32(6, serializer); sse_encode_String(err, serializer); - case PaymentError_InvalidOrExpiredFees(): + case PaymentError_Generic(err: final err): sse_encode_i_32(7, serializer); - case PaymentError_InsufficientFunds(): + sse_encode_String(err, serializer); + case PaymentError_InvalidOrExpiredFees(): sse_encode_i_32(8, serializer); - case PaymentError_InvalidDescription(err: final err): + case PaymentError_InsufficientFunds(): sse_encode_i_32(9, serializer); + case PaymentError_InvalidDescription(err: final err): + sse_encode_i_32(10, serializer); sse_encode_String(err, serializer); case PaymentError_InvalidInvoice(err: final err): - sse_encode_i_32(10, serializer); + sse_encode_i_32(11, serializer); sse_encode_String(err, serializer); case PaymentError_InvalidPreimage(): - sse_encode_i_32(11, serializer); - case PaymentError_LwkError(err: final err): sse_encode_i_32(12, serializer); + case PaymentError_LwkError(err: final err): + sse_encode_i_32(13, serializer); sse_encode_String(err, serializer); case PaymentError_PairsNotFound(): - sse_encode_i_32(13, serializer); - case PaymentError_PaymentTimeout(): sse_encode_i_32(14, serializer); - case PaymentError_PersistError(): + case PaymentError_PaymentTimeout(): sse_encode_i_32(15, serializer); - case PaymentError_ReceiveError(err: final err): + case PaymentError_PersistError(): sse_encode_i_32(16, serializer); + case PaymentError_ReceiveError(err: final err): + sse_encode_i_32(17, serializer); sse_encode_String(err, serializer); case PaymentError_Refunded(err: final err, refundTxId: final refundTxId): - sse_encode_i_32(17, serializer); + sse_encode_i_32(18, serializer); sse_encode_String(err, serializer); sse_encode_String(refundTxId, serializer); case PaymentError_SelfTransferNotSupported(): - sse_encode_i_32(18, serializer); - case PaymentError_SendError(err: final err): sse_encode_i_32(19, serializer); + case PaymentError_SendError(err: final err): + sse_encode_i_32(20, serializer); sse_encode_String(err, serializer); case PaymentError_SignerError(err: final err): - sse_encode_i_32(20, serializer); + sse_encode_i_32(21, serializer); sse_encode_String(err, serializer); } } @@ -7288,7 +7487,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { case ReceiveAmount_Asset(assetId: final assetId, payerAmount: final payerAmount): sse_encode_i_32(1, serializer); sse_encode_String(assetId, serializer); - sse_encode_opt_box_autoadd_u_64(payerAmount, serializer); + sse_encode_opt_box_autoadd_f_64(payerAmount, serializer); } } diff --git a/packages/dart/lib/src/frb_generated.io.dart b/packages/dart/lib/src/frb_generated.io.dart index d2eaa8ad2..e63713d0f 100644 --- a/packages/dart/lib/src/frb_generated.io.dart +++ b/packages/dart/lib/src/frb_generated.io.dart @@ -68,6 +68,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected AssetBalance dco_decode_asset_balance(dynamic raw); + @protected + AssetInfo dco_decode_asset_info(dynamic raw); + + @protected + AssetMetadata dco_decode_asset_metadata(dynamic raw); + @protected BackupRequest dco_decode_backup_request(dynamic raw); @@ -98,6 +104,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected Amount dco_decode_box_autoadd_amount(dynamic raw); + @protected + AssetInfo dco_decode_box_autoadd_asset_info(dynamic raw); + @protected BackupRequest dco_decode_box_autoadd_backup_request(dynamic raw); @@ -314,6 +323,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected List dco_decode_list_asset_balance(dynamic raw); + @protected + List dco_decode_list_asset_metadata(dynamic raw); + @protected List dco_decode_list_external_input_parser(dynamic raw); @@ -440,6 +452,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected Amount? dco_decode_opt_box_autoadd_amount(dynamic raw); + @protected + AssetInfo? dco_decode_opt_box_autoadd_asset_info(dynamic raw); + @protected bool? dco_decode_opt_box_autoadd_bool(dynamic raw); @@ -479,6 +494,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected BigInt? dco_decode_opt_box_autoadd_u_64(dynamic raw); + @protected + List? dco_decode_opt_list_asset_metadata(dynamic raw); + @protected List? dco_decode_opt_list_external_input_parser(dynamic raw); @@ -680,6 +698,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected AssetBalance sse_decode_asset_balance(SseDeserializer deserializer); + @protected + AssetInfo sse_decode_asset_info(SseDeserializer deserializer); + + @protected + AssetMetadata sse_decode_asset_metadata(SseDeserializer deserializer); + @protected BackupRequest sse_decode_backup_request(SseDeserializer deserializer); @@ -713,6 +737,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected Amount sse_decode_box_autoadd_amount(SseDeserializer deserializer); + @protected + AssetInfo sse_decode_box_autoadd_asset_info(SseDeserializer deserializer); + @protected BackupRequest sse_decode_box_autoadd_backup_request(SseDeserializer deserializer); @@ -932,6 +959,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected List sse_decode_list_asset_balance(SseDeserializer deserializer); + @protected + List sse_decode_list_asset_metadata(SseDeserializer deserializer); + @protected List sse_decode_list_external_input_parser(SseDeserializer deserializer); @@ -1058,6 +1088,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected Amount? sse_decode_opt_box_autoadd_amount(SseDeserializer deserializer); + @protected + AssetInfo? sse_decode_opt_box_autoadd_asset_info(SseDeserializer deserializer); + @protected bool? sse_decode_opt_box_autoadd_bool(SseDeserializer deserializer); @@ -1097,6 +1130,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected BigInt? sse_decode_opt_box_autoadd_u_64(SseDeserializer deserializer); + @protected + List? sse_decode_opt_list_asset_metadata(SseDeserializer deserializer); + @protected List? sse_decode_opt_list_external_input_parser(SseDeserializer deserializer); @@ -1331,6 +1367,14 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { return ptr; } + @protected + ffi.Pointer cst_encode_box_autoadd_asset_info(AssetInfo raw) { + // Codec=Cst (C-struct based), see doc to use other codecs + final ptr = wire.cst_new_box_autoadd_asset_info(); + cst_api_fill_to_wire_asset_info(raw, ptr.ref); + return ptr; + } + @protected ffi.Pointer cst_encode_box_autoadd_backup_request(BackupRequest raw) { // Codec=Cst (C-struct based), see doc to use other codecs @@ -1768,6 +1812,16 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { return ans; } + @protected + ffi.Pointer cst_encode_list_asset_metadata(List raw) { + // Codec=Cst (C-struct based), see doc to use other codecs + final ans = wire.cst_new_list_asset_metadata(raw.length); + for (var i = 0; i < raw.length; ++i) { + cst_api_fill_to_wire_asset_metadata(raw[i], ans.ref.ptr[i]); + } + return ans; + } + @protected ffi.Pointer cst_encode_list_external_input_parser( List raw) { @@ -1910,6 +1964,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { return raw == null ? ffi.nullptr : cst_encode_box_autoadd_amount(raw); } + @protected + ffi.Pointer cst_encode_opt_box_autoadd_asset_info(AssetInfo? raw) { + // Codec=Cst (C-struct based), see doc to use other codecs + return raw == null ? ffi.nullptr : cst_encode_box_autoadd_asset_info(raw); + } + @protected ffi.Pointer cst_encode_opt_box_autoadd_bool(bool? raw) { // Codec=Cst (C-struct based), see doc to use other codecs @@ -1990,6 +2050,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { return raw == null ? ffi.nullptr : cst_encode_box_autoadd_u_64(raw); } + @protected + ffi.Pointer cst_encode_opt_list_asset_metadata(List? raw) { + // Codec=Cst (C-struct based), see doc to use other codecs + return raw == null ? ffi.nullptr : cst_encode_list_asset_metadata(raw); + } + @protected ffi.Pointer cst_encode_opt_list_external_input_parser( List? raw) { @@ -2080,7 +2146,25 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void cst_api_fill_to_wire_asset_balance(AssetBalance apiObj, wire_cst_asset_balance wireObj) { wireObj.asset_id = cst_encode_String(apiObj.assetId); - wireObj.balance = cst_encode_u_64(apiObj.balance); + wireObj.balance_sat = cst_encode_u_64(apiObj.balanceSat); + wireObj.name = cst_encode_opt_String(apiObj.name); + wireObj.ticker = cst_encode_opt_String(apiObj.ticker); + wireObj.balance = cst_encode_opt_box_autoadd_f_64(apiObj.balance); + } + + @protected + void cst_api_fill_to_wire_asset_info(AssetInfo apiObj, wire_cst_asset_info wireObj) { + wireObj.name = cst_encode_String(apiObj.name); + wireObj.ticker = cst_encode_String(apiObj.ticker); + wireObj.amount = cst_encode_f_64(apiObj.amount); + } + + @protected + void cst_api_fill_to_wire_asset_metadata(AssetMetadata apiObj, wire_cst_asset_metadata wireObj) { + wireObj.asset_id = cst_encode_String(apiObj.assetId); + wireObj.name = cst_encode_String(apiObj.name); + wireObj.ticker = cst_encode_String(apiObj.ticker); + wireObj.precision = cst_encode_u_8(apiObj.precision); } @protected @@ -2140,6 +2224,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { cst_api_fill_to_wire_amount(apiObj, wireObj.ref); } + @protected + void cst_api_fill_to_wire_box_autoadd_asset_info( + AssetInfo apiObj, ffi.Pointer wireObj) { + cst_api_fill_to_wire_asset_info(apiObj, wireObj.ref); + } + @protected void cst_api_fill_to_wire_box_autoadd_backup_request( BackupRequest apiObj, ffi.Pointer wireObj) { @@ -2439,6 +2529,7 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { wireObj.use_default_external_input_parsers = cst_encode_bool(apiObj.useDefaultExternalInputParsers); wireObj.onchain_fee_rate_leeway_sat_per_vbyte = cst_encode_opt_box_autoadd_u_32(apiObj.onchainFeeRateLeewaySatPerVbyte); + wireObj.asset_metadata = cst_encode_opt_list_asset_metadata(apiObj.assetMetadata); } @protected @@ -2588,6 +2679,7 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { wireObj.address = cst_encode_String(apiObj.address); wireObj.network = cst_encode_network(apiObj.network); wireObj.asset_id = cst_encode_opt_String(apiObj.assetId); + wireObj.amount = cst_encode_opt_box_autoadd_f_64(apiObj.amount); wireObj.amount_sat = cst_encode_opt_box_autoadd_u_64(apiObj.amountSat); wireObj.label = cst_encode_opt_String(apiObj.label); wireObj.message = cst_encode_opt_String(apiObj.message); @@ -2982,7 +3074,7 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { } if (apiObj is PayAmount_Asset) { var pre_asset_id = cst_encode_String(apiObj.assetId); - var pre_receiver_amount = cst_encode_u_64(apiObj.receiverAmount); + var pre_receiver_amount = cst_encode_f_64(apiObj.receiverAmount); wireObj.tag = 1; wireObj.kind.Asset.asset_id = pre_asset_id; wireObj.kind.Asset.receiver_amount = pre_receiver_amount; @@ -3047,10 +3139,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { var pre_destination = cst_encode_String(apiObj.destination); var pre_description = cst_encode_String(apiObj.description); var pre_asset_id = cst_encode_String(apiObj.assetId); + var pre_asset_info = cst_encode_opt_box_autoadd_asset_info(apiObj.assetInfo); wireObj.tag = 1; wireObj.kind.Liquid.destination = pre_destination; wireObj.kind.Liquid.description = pre_description; wireObj.kind.Liquid.asset_id = pre_asset_id; + wireObj.kind.Liquid.asset_info = pre_asset_info; return; } if (apiObj is PaymentDetails_Bitcoin) { @@ -3099,87 +3193,93 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { wireObj.kind.AmountMissing.err = pre_err; return; } - if (apiObj is PaymentError_InvalidNetwork) { + if (apiObj is PaymentError_AssetError) { var pre_err = cst_encode_String(apiObj.err); wireObj.tag = 5; + wireObj.kind.AssetError.err = pre_err; + return; + } + if (apiObj is PaymentError_InvalidNetwork) { + var pre_err = cst_encode_String(apiObj.err); + wireObj.tag = 6; wireObj.kind.InvalidNetwork.err = pre_err; return; } if (apiObj is PaymentError_Generic) { var pre_err = cst_encode_String(apiObj.err); - wireObj.tag = 6; + wireObj.tag = 7; wireObj.kind.Generic.err = pre_err; return; } if (apiObj is PaymentError_InvalidOrExpiredFees) { - wireObj.tag = 7; + wireObj.tag = 8; return; } if (apiObj is PaymentError_InsufficientFunds) { - wireObj.tag = 8; + wireObj.tag = 9; return; } if (apiObj is PaymentError_InvalidDescription) { var pre_err = cst_encode_String(apiObj.err); - wireObj.tag = 9; + wireObj.tag = 10; wireObj.kind.InvalidDescription.err = pre_err; return; } if (apiObj is PaymentError_InvalidInvoice) { var pre_err = cst_encode_String(apiObj.err); - wireObj.tag = 10; + wireObj.tag = 11; wireObj.kind.InvalidInvoice.err = pre_err; return; } if (apiObj is PaymentError_InvalidPreimage) { - wireObj.tag = 11; + wireObj.tag = 12; return; } if (apiObj is PaymentError_LwkError) { var pre_err = cst_encode_String(apiObj.err); - wireObj.tag = 12; + wireObj.tag = 13; wireObj.kind.LwkError.err = pre_err; return; } if (apiObj is PaymentError_PairsNotFound) { - wireObj.tag = 13; + wireObj.tag = 14; return; } if (apiObj is PaymentError_PaymentTimeout) { - wireObj.tag = 14; + wireObj.tag = 15; return; } if (apiObj is PaymentError_PersistError) { - wireObj.tag = 15; + wireObj.tag = 16; return; } if (apiObj is PaymentError_ReceiveError) { var pre_err = cst_encode_String(apiObj.err); - wireObj.tag = 16; + wireObj.tag = 17; wireObj.kind.ReceiveError.err = pre_err; return; } if (apiObj is PaymentError_Refunded) { var pre_err = cst_encode_String(apiObj.err); var pre_refund_tx_id = cst_encode_String(apiObj.refundTxId); - wireObj.tag = 17; + wireObj.tag = 18; wireObj.kind.Refunded.err = pre_err; wireObj.kind.Refunded.refund_tx_id = pre_refund_tx_id; return; } if (apiObj is PaymentError_SelfTransferNotSupported) { - wireObj.tag = 18; + wireObj.tag = 19; return; } if (apiObj is PaymentError_SendError) { var pre_err = cst_encode_String(apiObj.err); - wireObj.tag = 19; + wireObj.tag = 20; wireObj.kind.SendError.err = pre_err; return; } if (apiObj is PaymentError_SignerError) { var pre_err = cst_encode_String(apiObj.err); - wireObj.tag = 20; + wireObj.tag = 21; wireObj.kind.SignerError.err = pre_err; return; } @@ -3298,7 +3398,7 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { } if (apiObj is ReceiveAmount_Asset) { var pre_asset_id = cst_encode_String(apiObj.assetId); - var pre_payer_amount = cst_encode_opt_box_autoadd_u_64(apiObj.payerAmount); + var pre_payer_amount = cst_encode_opt_box_autoadd_f_64(apiObj.payerAmount); wireObj.tag = 1; wireObj.kind.Asset.asset_id = pre_asset_id; wireObj.kind.Asset.payer_amount = pre_payer_amount; @@ -3664,6 +3764,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_asset_balance(AssetBalance self, SseSerializer serializer); + @protected + void sse_encode_asset_info(AssetInfo self, SseSerializer serializer); + + @protected + void sse_encode_asset_metadata(AssetMetadata self, SseSerializer serializer); + @protected void sse_encode_backup_request(BackupRequest self, SseSerializer serializer); @@ -3697,6 +3803,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_box_autoadd_amount(Amount self, SseSerializer serializer); + @protected + void sse_encode_box_autoadd_asset_info(AssetInfo self, SseSerializer serializer); + @protected void sse_encode_box_autoadd_backup_request(BackupRequest self, SseSerializer serializer); @@ -3923,6 +4032,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_list_asset_balance(List self, SseSerializer serializer); + @protected + void sse_encode_list_asset_metadata(List self, SseSerializer serializer); + @protected void sse_encode_list_external_input_parser(List self, SseSerializer serializer); @@ -4050,6 +4162,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_opt_box_autoadd_amount(Amount? self, SseSerializer serializer); + @protected + void sse_encode_opt_box_autoadd_asset_info(AssetInfo? self, SseSerializer serializer); + @protected void sse_encode_opt_box_autoadd_bool(bool? self, SseSerializer serializer); @@ -4090,6 +4205,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_opt_box_autoadd_u_64(BigInt? self, SseSerializer serializer); + @protected + void sse_encode_opt_list_asset_metadata(List? self, SseSerializer serializer); + @protected void sse_encode_opt_list_external_input_parser(List? self, SseSerializer serializer); @@ -5140,6 +5258,16 @@ class RustLibWire implements BaseWire { late final _cst_new_box_autoadd_amount = _cst_new_box_autoadd_amountPtr.asFunction Function()>(); + ffi.Pointer cst_new_box_autoadd_asset_info() { + return _cst_new_box_autoadd_asset_info(); + } + + late final _cst_new_box_autoadd_asset_infoPtr = + _lookup Function()>>( + 'frbgen_breez_liquid_cst_new_box_autoadd_asset_info'); + late final _cst_new_box_autoadd_asset_info = + _cst_new_box_autoadd_asset_infoPtr.asFunction Function()>(); + ffi.Pointer cst_new_box_autoadd_backup_request() { return _cst_new_box_autoadd_backup_request(); } @@ -5686,6 +5814,20 @@ class RustLibWire implements BaseWire { late final _cst_new_list_asset_balance = _cst_new_list_asset_balancePtr.asFunction Function(int)>(); + ffi.Pointer cst_new_list_asset_metadata( + int len, + ) { + return _cst_new_list_asset_metadata( + len, + ); + } + + late final _cst_new_list_asset_metadataPtr = + _lookup Function(ffi.Int32)>>( + 'frbgen_breez_liquid_cst_new_list_asset_metadata'); + late final _cst_new_list_asset_metadata = + _cst_new_list_asset_metadataPtr.asFunction Function(int)>(); + ffi.Pointer cst_new_list_external_input_parser( int len, ) { @@ -6030,6 +6172,8 @@ final class wire_cst_liquid_address_data extends ffi.Struct { external ffi.Pointer asset_id; + external ffi.Pointer amount; + external ffi.Pointer amount_sat; external ffi.Pointer label; @@ -6351,8 +6495,8 @@ final class wire_cst_PayAmount_Bitcoin extends ffi.Struct { final class wire_cst_PayAmount_Asset extends ffi.Struct { external ffi.Pointer asset_id; - @ffi.Uint64() - external int receiver_amount; + @ffi.Double() + external double receiver_amount; } final class PayAmountKind extends ffi.Union { @@ -6382,7 +6526,7 @@ final class wire_cst_ReceiveAmount_Bitcoin extends ffi.Struct { final class wire_cst_ReceiveAmount_Asset extends ffi.Struct { external ffi.Pointer asset_id; - external ffi.Pointer payer_amount; + external ffi.Pointer payer_amount; } final class ReceiveAmountKind extends ffi.Union { @@ -6571,12 +6715,23 @@ final class wire_cst_PaymentDetails_Lightning extends ffi.Struct { external ffi.Pointer refund_tx_amount_sat; } +final class wire_cst_asset_info extends ffi.Struct { + external ffi.Pointer name; + + external ffi.Pointer ticker; + + @ffi.Double() + external double amount; +} + final class wire_cst_PaymentDetails_Liquid extends ffi.Struct { external ffi.Pointer destination; external ffi.Pointer description; external ffi.Pointer asset_id; + + external ffi.Pointer asset_info; } final class wire_cst_PaymentDetails_Bitcoin extends ffi.Struct { @@ -6710,6 +6865,24 @@ final class wire_cst_list_external_input_parser extends ffi.Struct { external int len; } +final class wire_cst_asset_metadata extends ffi.Struct { + external ffi.Pointer asset_id; + + external ffi.Pointer name; + + external ffi.Pointer ticker; + + @ffi.Uint8() + external int precision; +} + +final class wire_cst_list_asset_metadata extends ffi.Struct { + external ffi.Pointer ptr; + + @ffi.Int32() + external int len; +} + final class wire_cst_config extends ffi.Struct { external ffi.Pointer liquid_electrum_url; @@ -6742,6 +6915,8 @@ final class wire_cst_config extends ffi.Struct { external bool use_default_external_input_parsers; external ffi.Pointer onchain_fee_rate_leeway_sat_per_vbyte; + + external ffi.Pointer asset_metadata; } final class wire_cst_connect_request extends ffi.Struct { @@ -6797,7 +6972,13 @@ final class wire_cst_asset_balance extends ffi.Struct { external ffi.Pointer asset_id; @ffi.Uint64() - external int balance; + external int balance_sat; + + external ffi.Pointer name; + + external ffi.Pointer ticker; + + external ffi.Pointer balance; } final class wire_cst_list_asset_balance extends ffi.Struct { @@ -7259,6 +7440,10 @@ final class wire_cst_PaymentError_AmountMissing extends ffi.Struct { external ffi.Pointer err; } +final class wire_cst_PaymentError_AssetError extends ffi.Struct { + external ffi.Pointer err; +} + final class wire_cst_PaymentError_InvalidNetwork extends ffi.Struct { external ffi.Pointer err; } @@ -7300,6 +7485,8 @@ final class wire_cst_PaymentError_SignerError extends ffi.Struct { final class PaymentErrorKind extends ffi.Union { external wire_cst_PaymentError_AmountMissing AmountMissing; + external wire_cst_PaymentError_AssetError AssetError; + external wire_cst_PaymentError_InvalidNetwork InvalidNetwork; external wire_cst_PaymentError_Generic Generic; diff --git a/packages/dart/lib/src/model.dart b/packages/dart/lib/src/model.dart index 3d4c31988..fa623cd70 100644 --- a/packages/dart/lib/src/model.dart +++ b/packages/dart/lib/src/model.dart @@ -31,15 +31,22 @@ class AcceptPaymentProposedFeesRequest { /// An asset balance to denote the balance for each asset. class AssetBalance { final String assetId; - final BigInt balance; + final BigInt balanceSat; + final String? name; + final String? ticker; + final double? balance; const AssetBalance({ required this.assetId, - required this.balance, + required this.balanceSat, + this.name, + this.ticker, + this.balance, }); @override - int get hashCode => assetId.hashCode ^ balance.hashCode; + int get hashCode => + assetId.hashCode ^ balanceSat.hashCode ^ name.hashCode ^ ticker.hashCode ^ balance.hashCode; @override bool operator ==(Object other) => @@ -47,9 +54,82 @@ class AssetBalance { other is AssetBalance && runtimeType == other.runtimeType && assetId == other.assetId && + balanceSat == other.balanceSat && + name == other.name && + ticker == other.ticker && balance == other.balance; } +/// Represents the Liquid payment asset info. The asset info is derived from +/// the available [AssetMetadata] that is set in the [Config]. +class AssetInfo { + /// The name of the asset + final String name; + + /// The ticker of the asset + final String ticker; + + /// The amount calculated from the satoshi amount of the transaction, having its + /// decimal shifted to the left by the [precision](AssetMetadata::precision) + final double amount; + + const AssetInfo({ + required this.name, + required this.ticker, + required this.amount, + }); + + @override + int get hashCode => name.hashCode ^ ticker.hashCode ^ amount.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is AssetInfo && + runtimeType == other.runtimeType && + name == other.name && + ticker == other.ticker && + amount == other.amount; +} + +/// Configuration for asset metadata. Each asset metadata item represents an entry in the +/// [Liquid Asset Registry](https://docs.liquid.net/docs/blockstream-liquid-asset-registry). +/// An example Liquid Asset in the registry would be [Tether USD](https://assets.blockstream.info/ce091c998b83c78bb71a632313ba3760f1763d9cfcffae02258ffa9865a37bd2.json>). +class AssetMetadata { + /// The asset id of the registered asset + final String assetId; + + /// The name of the asset + final String name; + + /// The ticker of the asset + final String ticker; + + /// The precision used to display the asset amount. + /// For example, precision of 2 shifts the decimal 2 places left from the satoshi amount. + final int precision; + + const AssetMetadata({ + required this.assetId, + required this.name, + required this.ticker, + required this.precision, + }); + + @override + int get hashCode => assetId.hashCode ^ name.hashCode ^ ticker.hashCode ^ precision.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is AssetMetadata && + runtimeType == other.runtimeType && + assetId == other.assetId && + name == other.name && + ticker == other.ticker && + precision == other.precision; +} + /// An argument when calling [crate::sdk::LiquidSdk::backup]. class BackupRequest { /// Path to the backup. @@ -225,6 +305,12 @@ class Config { /// Defaults to zero. final int? onchainFeeRateLeewaySatPerVbyte; + /// A set of asset metadata used by [LiquidSdk::parse](crate::sdk::LiquidSdk::parse) when the input is a + /// [LiquidAddressData] and the [asset_id](LiquidAddressData::asset_id) differs from the Liquid Bitcoin asset. + /// See [AssetMetadata] for more details on how define asset metadata. + /// By default the asset metadata for Liquid Bitcoin and Tether USD are included. + final List? assetMetadata; + const Config({ required this.liquidElectrumUrl, required this.bitcoinElectrumUrl, @@ -240,6 +326,7 @@ class Config { this.externalInputParsers, required this.useDefaultExternalInputParsers, this.onchainFeeRateLeewaySatPerVbyte, + this.assetMetadata, }); @override @@ -257,7 +344,8 @@ class Config { breezApiKey.hashCode ^ externalInputParsers.hashCode ^ useDefaultExternalInputParsers.hashCode ^ - onchainFeeRateLeewaySatPerVbyte.hashCode; + onchainFeeRateLeewaySatPerVbyte.hashCode ^ + assetMetadata.hashCode; @override bool operator ==(Object other) => @@ -277,7 +365,8 @@ class Config { breezApiKey == other.breezApiKey && externalInputParsers == other.externalInputParsers && useDefaultExternalInputParsers == other.useDefaultExternalInputParsers && - onchainFeeRateLeewaySatPerVbyte == other.onchainFeeRateLeewaySatPerVbyte; + onchainFeeRateLeewaySatPerVbyte == other.onchainFeeRateLeewaySatPerVbyte && + assetMetadata == other.assetMetadata; } /// An argument when calling [crate::sdk::LiquidSdk::connect]. @@ -676,7 +765,7 @@ sealed class PayAmount with _$PayAmount { /// The amount of an asset that will be received const factory PayAmount.asset({ required String assetId, - required BigInt receiverAmount, + required double receiverAmount, }) = PayAmount_Asset; /// Indicates that all available Bitcoin funds should be sent @@ -715,7 +804,7 @@ class Payment { final String? txId; /// Data to use in the `blinded` param when unblinding the transaction in an explorer. - /// See: https://docs.liquid.net/docs/unblinding-transactions + /// See: final String? unblindingData; /// Composite timestamp that can be used for sorting or displaying the payment. @@ -855,6 +944,9 @@ sealed class PaymentDetails with _$PaymentDetails { /// The asset id required String assetId, + + /// The asset info derived from the [AssetMetadata] + AssetInfo? assetInfo, }) = PaymentDetails_Liquid; /// Swapping to or from the Bitcoin chain @@ -1358,7 +1450,7 @@ sealed class ReceiveAmount with _$ReceiveAmount { /// The amount of an asset that should be paid const factory ReceiveAmount.asset({ required String assetId, - BigInt? payerAmount, + double? payerAmount, }) = ReceiveAmount_Asset; } diff --git a/packages/dart/lib/src/model.freezed.dart b/packages/dart/lib/src/model.freezed.dart index 7a4dcef20..fa3caf50f 100644 --- a/packages/dart/lib/src/model.freezed.dart +++ b/packages/dart/lib/src/model.freezed.dart @@ -717,7 +717,7 @@ abstract class _$$PayAmount_AssetImplCopyWith<$Res> { _$PayAmount_AssetImpl value, $Res Function(_$PayAmount_AssetImpl) then) = __$$PayAmount_AssetImplCopyWithImpl<$Res>; @useResult - $Res call({String assetId, BigInt receiverAmount}); + $Res call({String assetId, double receiverAmount}); } /// @nodoc @@ -743,7 +743,7 @@ class __$$PayAmount_AssetImplCopyWithImpl<$Res> extends _$PayAmountCopyWithImpl< receiverAmount: null == receiverAmount ? _value.receiverAmount : receiverAmount // ignore: cast_nullable_to_non_nullable - as BigInt, + as double, )); } } @@ -756,7 +756,7 @@ class _$PayAmount_AssetImpl extends PayAmount_Asset { @override final String assetId; @override - final BigInt receiverAmount; + final double receiverAmount; @override String toString() { @@ -785,12 +785,12 @@ class _$PayAmount_AssetImpl extends PayAmount_Asset { } abstract class PayAmount_Asset extends PayAmount { - const factory PayAmount_Asset({required final String assetId, required final BigInt receiverAmount}) = + const factory PayAmount_Asset({required final String assetId, required final double receiverAmount}) = _$PayAmount_AssetImpl; const PayAmount_Asset._() : super._(); String get assetId; - BigInt get receiverAmount; + double get receiverAmount; /// Create a copy of PayAmount /// with the given fields replaced by the non-null parameter values. @@ -1142,7 +1142,7 @@ abstract class _$$PaymentDetails_LiquidImplCopyWith<$Res> implements $PaymentDet __$$PaymentDetails_LiquidImplCopyWithImpl<$Res>; @override @useResult - $Res call({String destination, String description, String assetId}); + $Res call({String destination, String description, String assetId, AssetInfo? assetInfo}); } /// @nodoc @@ -1161,6 +1161,7 @@ class __$$PaymentDetails_LiquidImplCopyWithImpl<$Res> Object? destination = null, Object? description = null, Object? assetId = null, + Object? assetInfo = freezed, }) { return _then(_$PaymentDetails_LiquidImpl( destination: null == destination @@ -1175,6 +1176,10 @@ class __$$PaymentDetails_LiquidImplCopyWithImpl<$Res> ? _value.assetId : assetId // ignore: cast_nullable_to_non_nullable as String, + assetInfo: freezed == assetInfo + ? _value.assetInfo + : assetInfo // ignore: cast_nullable_to_non_nullable + as AssetInfo?, )); } } @@ -1183,7 +1188,7 @@ class __$$PaymentDetails_LiquidImplCopyWithImpl<$Res> class _$PaymentDetails_LiquidImpl extends PaymentDetails_Liquid { const _$PaymentDetails_LiquidImpl( - {required this.destination, required this.description, required this.assetId}) + {required this.destination, required this.description, required this.assetId, this.assetInfo}) : super._(); /// Represents either a Liquid BIP21 URI or pure address @@ -1198,9 +1203,13 @@ class _$PaymentDetails_LiquidImpl extends PaymentDetails_Liquid { @override final String assetId; + /// The asset info derived from the [AssetMetadata] + @override + final AssetInfo? assetInfo; + @override String toString() { - return 'PaymentDetails.liquid(destination: $destination, description: $description, assetId: $assetId)'; + return 'PaymentDetails.liquid(destination: $destination, description: $description, assetId: $assetId, assetInfo: $assetInfo)'; } @override @@ -1210,11 +1219,12 @@ class _$PaymentDetails_LiquidImpl extends PaymentDetails_Liquid { other is _$PaymentDetails_LiquidImpl && (identical(other.destination, destination) || other.destination == destination) && (identical(other.description, description) || other.description == description) && - (identical(other.assetId, assetId) || other.assetId == assetId)); + (identical(other.assetId, assetId) || other.assetId == assetId) && + (identical(other.assetInfo, assetInfo) || other.assetInfo == assetInfo)); } @override - int get hashCode => Object.hash(runtimeType, destination, description, assetId); + int get hashCode => Object.hash(runtimeType, destination, description, assetId, assetInfo); /// Create a copy of PaymentDetails /// with the given fields replaced by the non-null parameter values. @@ -1229,7 +1239,8 @@ abstract class PaymentDetails_Liquid extends PaymentDetails { const factory PaymentDetails_Liquid( {required final String destination, required final String description, - required final String assetId}) = _$PaymentDetails_LiquidImpl; + required final String assetId, + final AssetInfo? assetInfo}) = _$PaymentDetails_LiquidImpl; const PaymentDetails_Liquid._() : super._(); /// Represents either a Liquid BIP21 URI or pure address @@ -1242,6 +1253,9 @@ abstract class PaymentDetails_Liquid extends PaymentDetails { /// The asset id String get assetId; + /// The asset info derived from the [AssetMetadata] + AssetInfo? get assetInfo; + /// Create a copy of PaymentDetails /// with the given fields replaced by the non-null parameter values. @override @@ -1552,7 +1566,7 @@ abstract class _$$ReceiveAmount_AssetImplCopyWith<$Res> { _$ReceiveAmount_AssetImpl value, $Res Function(_$ReceiveAmount_AssetImpl) then) = __$$ReceiveAmount_AssetImplCopyWithImpl<$Res>; @useResult - $Res call({String assetId, BigInt? payerAmount}); + $Res call({String assetId, double? payerAmount}); } /// @nodoc @@ -1579,7 +1593,7 @@ class __$$ReceiveAmount_AssetImplCopyWithImpl<$Res> payerAmount: freezed == payerAmount ? _value.payerAmount : payerAmount // ignore: cast_nullable_to_non_nullable - as BigInt?, + as double?, )); } } @@ -1592,7 +1606,7 @@ class _$ReceiveAmount_AssetImpl extends ReceiveAmount_Asset { @override final String assetId; @override - final BigInt? payerAmount; + final double? payerAmount; @override String toString() { @@ -1621,12 +1635,12 @@ class _$ReceiveAmount_AssetImpl extends ReceiveAmount_Asset { } abstract class ReceiveAmount_Asset extends ReceiveAmount { - const factory ReceiveAmount_Asset({required final String assetId, final BigInt? payerAmount}) = + const factory ReceiveAmount_Asset({required final String assetId, final double? payerAmount}) = _$ReceiveAmount_AssetImpl; const ReceiveAmount_Asset._() : super._(); String get assetId; - BigInt? get payerAmount; + double? get payerAmount; /// Create a copy of ReceiveAmount /// with the given fields replaced by the non-null parameter values. diff --git a/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart b/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart index 92fb1ff07..0e3309c6f 100644 --- a/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart +++ b/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart @@ -916,6 +916,17 @@ class FlutterBreezLiquidBindings { _frbgen_breez_liquid_cst_new_box_autoadd_amountPtr .asFunction Function()>(); + ffi.Pointer frbgen_breez_liquid_cst_new_box_autoadd_asset_info() { + return _frbgen_breez_liquid_cst_new_box_autoadd_asset_info(); + } + + late final _frbgen_breez_liquid_cst_new_box_autoadd_asset_infoPtr = + _lookup Function()>>( + 'frbgen_breez_liquid_cst_new_box_autoadd_asset_info'); + late final _frbgen_breez_liquid_cst_new_box_autoadd_asset_info = + _frbgen_breez_liquid_cst_new_box_autoadd_asset_infoPtr + .asFunction Function()>(); + ffi.Pointer frbgen_breez_liquid_cst_new_box_autoadd_backup_request() { return _frbgen_breez_liquid_cst_new_box_autoadd_backup_request(); } @@ -1519,6 +1530,21 @@ class FlutterBreezLiquidBindings { _frbgen_breez_liquid_cst_new_list_asset_balancePtr .asFunction Function(int)>(); + ffi.Pointer frbgen_breez_liquid_cst_new_list_asset_metadata( + int len, + ) { + return _frbgen_breez_liquid_cst_new_list_asset_metadata( + len, + ); + } + + late final _frbgen_breez_liquid_cst_new_list_asset_metadataPtr = + _lookup Function(ffi.Int32)>>( + 'frbgen_breez_liquid_cst_new_list_asset_metadata'); + late final _frbgen_breez_liquid_cst_new_list_asset_metadata = + _frbgen_breez_liquid_cst_new_list_asset_metadataPtr + .asFunction Function(int)>(); + ffi.Pointer frbgen_breez_liquid_cst_new_list_external_input_parser( int len, ) { @@ -4240,6 +4266,8 @@ final class wire_cst_liquid_address_data extends ffi.Struct { external ffi.Pointer asset_id; + external ffi.Pointer amount; + external ffi.Pointer amount_sat; external ffi.Pointer label; @@ -4561,8 +4589,8 @@ final class wire_cst_PayAmount_Bitcoin extends ffi.Struct { final class wire_cst_PayAmount_Asset extends ffi.Struct { external ffi.Pointer asset_id; - @ffi.Uint64() - external int receiver_amount; + @ffi.Double() + external double receiver_amount; } final class PayAmountKind extends ffi.Union { @@ -4592,7 +4620,7 @@ final class wire_cst_ReceiveAmount_Bitcoin extends ffi.Struct { final class wire_cst_ReceiveAmount_Asset extends ffi.Struct { external ffi.Pointer asset_id; - external ffi.Pointer payer_amount; + external ffi.Pointer payer_amount; } final class ReceiveAmountKind extends ffi.Union { @@ -4781,12 +4809,23 @@ final class wire_cst_PaymentDetails_Lightning extends ffi.Struct { external ffi.Pointer refund_tx_amount_sat; } +final class wire_cst_asset_info extends ffi.Struct { + external ffi.Pointer name; + + external ffi.Pointer ticker; + + @ffi.Double() + external double amount; +} + final class wire_cst_PaymentDetails_Liquid extends ffi.Struct { external ffi.Pointer destination; external ffi.Pointer description; external ffi.Pointer asset_id; + + external ffi.Pointer asset_info; } final class wire_cst_PaymentDetails_Bitcoin extends ffi.Struct { @@ -4920,6 +4959,24 @@ final class wire_cst_list_external_input_parser extends ffi.Struct { external int len; } +final class wire_cst_asset_metadata extends ffi.Struct { + external ffi.Pointer asset_id; + + external ffi.Pointer name; + + external ffi.Pointer ticker; + + @ffi.Uint8() + external int precision; +} + +final class wire_cst_list_asset_metadata extends ffi.Struct { + external ffi.Pointer ptr; + + @ffi.Int32() + external int len; +} + final class wire_cst_config extends ffi.Struct { external ffi.Pointer liquid_electrum_url; @@ -4952,6 +5009,8 @@ final class wire_cst_config extends ffi.Struct { external bool use_default_external_input_parsers; external ffi.Pointer onchain_fee_rate_leeway_sat_per_vbyte; + + external ffi.Pointer asset_metadata; } final class wire_cst_connect_request extends ffi.Struct { @@ -5007,7 +5066,13 @@ final class wire_cst_asset_balance extends ffi.Struct { external ffi.Pointer asset_id; @ffi.Uint64() - external int balance; + external int balance_sat; + + external ffi.Pointer name; + + external ffi.Pointer ticker; + + external ffi.Pointer balance; } final class wire_cst_list_asset_balance extends ffi.Struct { @@ -5469,6 +5534,10 @@ final class wire_cst_PaymentError_AmountMissing extends ffi.Struct { external ffi.Pointer err; } +final class wire_cst_PaymentError_AssetError extends ffi.Struct { + external ffi.Pointer err; +} + final class wire_cst_PaymentError_InvalidNetwork extends ffi.Struct { external ffi.Pointer err; } @@ -5510,6 +5579,8 @@ final class wire_cst_PaymentError_SignerError extends ffi.Struct { final class PaymentErrorKind extends ffi.Union { external wire_cst_PaymentError_AmountMissing AmountMissing; + external wire_cst_PaymentError_AssetError AssetError; + external wire_cst_PaymentError_InvalidNetwork InvalidNetwork; external wire_cst_PaymentError_Generic Generic; diff --git a/packages/react-native/android/src/main/java/com/breezsdkliquid/BreezSDKLiquidMapper.kt b/packages/react-native/android/src/main/java/com/breezsdkliquid/BreezSDKLiquidMapper.kt index dbaed89a4..886f90b43 100644 --- a/packages/react-native/android/src/main/java/com/breezsdkliquid/BreezSDKLiquidMapper.kt +++ b/packages/react-native/android/src/main/java/com/breezsdkliquid/BreezSDKLiquidMapper.kt @@ -107,20 +107,26 @@ fun asAssetBalance(assetBalance: ReadableMap): AssetBalance? { assetBalance, arrayOf( "assetId", - "balance", + "balanceSat", ), ) ) { return null } val assetId = assetBalance.getString("assetId")!! - val balance = assetBalance.getDouble("balance").toULong() - return AssetBalance(assetId, balance) + val balanceSat = assetBalance.getDouble("balanceSat").toULong() + val name = if (hasNonNullKey(assetBalance, "name")) assetBalance.getString("name") else null + val ticker = if (hasNonNullKey(assetBalance, "ticker")) assetBalance.getString("ticker") else null + val balance = if (hasNonNullKey(assetBalance, "balance")) assetBalance.getDouble("balance") else null + return AssetBalance(assetId, balanceSat, name, ticker, balance) } fun readableMapOf(assetBalance: AssetBalance): ReadableMap = readableMapOf( "assetId" to assetBalance.assetId, + "balanceSat" to assetBalance.balanceSat, + "name" to assetBalance.name, + "ticker" to assetBalance.ticker, "balance" to assetBalance.balance, ) @@ -135,6 +141,81 @@ fun asAssetBalanceList(arr: ReadableArray): List { return list } +fun asAssetInfo(assetInfo: ReadableMap): AssetInfo? { + if (!validateMandatoryFields( + assetInfo, + arrayOf( + "name", + "ticker", + "amount", + ), + ) + ) { + return null + } + val name = assetInfo.getString("name")!! + val ticker = assetInfo.getString("ticker")!! + val amount = assetInfo.getDouble("amount") + return AssetInfo(name, ticker, amount) +} + +fun readableMapOf(assetInfo: AssetInfo): ReadableMap = + readableMapOf( + "name" to assetInfo.name, + "ticker" to assetInfo.ticker, + "amount" to assetInfo.amount, + ) + +fun asAssetInfoList(arr: ReadableArray): List { + val list = ArrayList() + for (value in arr.toList()) { + when (value) { + is ReadableMap -> list.add(asAssetInfo(value)!!) + else -> throw SdkException.Generic(errUnexpectedType(value)) + } + } + return list +} + +fun asAssetMetadata(assetMetadata: ReadableMap): AssetMetadata? { + if (!validateMandatoryFields( + assetMetadata, + arrayOf( + "assetId", + "name", + "ticker", + "precision", + ), + ) + ) { + return null + } + val assetId = assetMetadata.getString("assetId")!! + val name = assetMetadata.getString("name")!! + val ticker = assetMetadata.getString("ticker")!! + val precision = assetMetadata.getInt("precision").toUByte() + return AssetMetadata(assetId, name, ticker, precision) +} + +fun readableMapOf(assetMetadata: AssetMetadata): ReadableMap = + readableMapOf( + "assetId" to assetMetadata.assetId, + "name" to assetMetadata.name, + "ticker" to assetMetadata.ticker, + "precision" to assetMetadata.precision, + ) + +fun asAssetMetadataList(arr: ReadableArray): List { + val list = ArrayList() + for (value in arr.toList()) { + when (value) { + is ReadableMap -> list.add(asAssetMetadata(value)!!) + else -> throw SdkException.Generic(errUnexpectedType(value)) + } + } + return list +} + fun asBackupRequest(backupRequest: ReadableMap): BackupRequest? { if (!validateMandatoryFields( backupRequest, @@ -389,6 +470,16 @@ fun asConfig(config: ReadableMap): Config? { } else { null } + val assetMetadata = + if (hasNonNullKey( + config, + "assetMetadata", + ) + ) { + config.getArray("assetMetadata")?.let { asAssetMetadataList(it) } + } else { + null + } return Config( liquidElectrumUrl, bitcoinElectrumUrl, @@ -404,6 +495,7 @@ fun asConfig(config: ReadableMap): Config? { useDefaultExternalInputParsers, externalInputParsers, onchainFeeRateLeewaySatPerVbyte, + assetMetadata, ) } @@ -423,6 +515,7 @@ fun readableMapOf(config: Config): ReadableMap = "useDefaultExternalInputParsers" to config.useDefaultExternalInputParsers, "externalInputParsers" to config.externalInputParsers?.let { readableArrayOf(it) }, "onchainFeeRateLeewaySatPerVbyte" to config.onchainFeeRateLeewaySatPerVbyte, + "assetMetadata" to config.assetMetadata?.let { readableArrayOf(it) }, ) fun asConfigList(arr: ReadableArray): List { @@ -917,10 +1010,11 @@ fun asLiquidAddressData(liquidAddressData: ReadableMap): LiquidAddressData? { val address = liquidAddressData.getString("address")!! val network = liquidAddressData.getString("network")?.let { asNetwork(it) }!! val assetId = if (hasNonNullKey(liquidAddressData, "assetId")) liquidAddressData.getString("assetId") else null + val amount = if (hasNonNullKey(liquidAddressData, "amount")) liquidAddressData.getDouble("amount") else null val amountSat = if (hasNonNullKey(liquidAddressData, "amountSat")) liquidAddressData.getDouble("amountSat").toULong() else null val label = if (hasNonNullKey(liquidAddressData, "label")) liquidAddressData.getString("label") else null val message = if (hasNonNullKey(liquidAddressData, "message")) liquidAddressData.getString("message") else null - return LiquidAddressData(address, network, assetId, amountSat, label, message) + return LiquidAddressData(address, network, assetId, amount, amountSat, label, message) } fun readableMapOf(liquidAddressData: LiquidAddressData): ReadableMap = @@ -928,6 +1022,7 @@ fun readableMapOf(liquidAddressData: LiquidAddressData): ReadableMap = "address" to liquidAddressData.address, "network" to liquidAddressData.network.name.lowercase(), "assetId" to liquidAddressData.assetId, + "amount" to liquidAddressData.amount, "amountSat" to liquidAddressData.amountSat, "label" to liquidAddressData.label, "message" to liquidAddressData.message, @@ -3249,7 +3344,7 @@ fun asPayAmount(payAmount: ReadableMap): PayAmount? { } if (type == "asset") { val assetId = payAmount.getString("assetId")!! - val receiverAmount = payAmount.getDouble("receiverAmount").toULong() + val receiverAmount = payAmount.getDouble("receiverAmount") return PayAmount.Asset(assetId, receiverAmount) } if (type == "drain") { @@ -3348,7 +3443,17 @@ fun asPaymentDetails(paymentDetails: ReadableMap): PaymentDetails? { val assetId = paymentDetails.getString("assetId")!! val destination = paymentDetails.getString("destination")!! val description = paymentDetails.getString("description")!! - return PaymentDetails.Liquid(assetId, destination, description) + val assetInfo = + if (hasNonNullKey( + paymentDetails, + "assetInfo", + ) + ) { + paymentDetails.getMap("assetInfo")?.let { asAssetInfo(it) } + } else { + null + } + return PaymentDetails.Liquid(assetId, destination, description, assetInfo) } if (type == "bitcoin") { val swapId = paymentDetails.getString("swapId")!! @@ -3420,6 +3525,7 @@ fun readableMapOf(paymentDetails: PaymentDetails): ReadableMap? { pushToMap(map, "assetId", paymentDetails.assetId) pushToMap(map, "destination", paymentDetails.destination) pushToMap(map, "description", paymentDetails.description) + pushToMap(map, "assetInfo", paymentDetails.assetInfo?.let { readableMapOf(it) }) } is PaymentDetails.Bitcoin -> { pushToMap(map, "type", "bitcoin") @@ -3494,7 +3600,7 @@ fun asReceiveAmount(receiveAmount: ReadableMap): ReceiveAmount? { } if (type == "asset") { val assetId = receiveAmount.getString("assetId")!! - val payerAmount = if (hasNonNullKey(receiveAmount, "payerAmount")) receiveAmount.getDouble("payerAmount").toULong() else null + val payerAmount = if (hasNonNullKey(receiveAmount, "payerAmount")) receiveAmount.getDouble("payerAmount") else null return ReceiveAmount.Asset(assetId, payerAmount) } return null @@ -3798,6 +3904,7 @@ fun pushToArray( when (value) { null -> array.pushNull() is AssetBalance -> array.pushMap(readableMapOf(value)) + is AssetMetadata -> array.pushMap(readableMapOf(value)) is ExternalInputParser -> array.pushMap(readableMapOf(value)) is FiatCurrency -> array.pushMap(readableMapOf(value)) is LnOfferBlindedPath -> array.pushMap(readableMapOf(value)) diff --git a/packages/react-native/ios/BreezSDKLiquidMapper.swift b/packages/react-native/ios/BreezSDKLiquidMapper.swift index e62cf40fe..cf6eeb611 100644 --- a/packages/react-native/ios/BreezSDKLiquidMapper.swift +++ b/packages/react-native/ios/BreezSDKLiquidMapper.swift @@ -112,17 +112,41 @@ enum BreezSDKLiquidMapper { guard let assetId = assetBalance["assetId"] as? String else { throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "assetId", typeName: "AssetBalance")) } - guard let balance = assetBalance["balance"] as? UInt64 else { - throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "balance", typeName: "AssetBalance")) + guard let balanceSat = assetBalance["balanceSat"] as? UInt64 else { + throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "balanceSat", typeName: "AssetBalance")) + } + var name: String? + if hasNonNilKey(data: assetBalance, key: "name") { + guard let nameTmp = assetBalance["name"] as? String else { + throw SdkError.Generic(message: errUnexpectedValue(fieldName: "name")) + } + name = nameTmp + } + var ticker: String? + if hasNonNilKey(data: assetBalance, key: "ticker") { + guard let tickerTmp = assetBalance["ticker"] as? String else { + throw SdkError.Generic(message: errUnexpectedValue(fieldName: "ticker")) + } + ticker = tickerTmp + } + var balance: Double? + if hasNonNilKey(data: assetBalance, key: "balance") { + guard let balanceTmp = assetBalance["balance"] as? Double else { + throw SdkError.Generic(message: errUnexpectedValue(fieldName: "balance")) + } + balance = balanceTmp } - return AssetBalance(assetId: assetId, balance: balance) + return AssetBalance(assetId: assetId, balanceSat: balanceSat, name: name, ticker: ticker, balance: balance) } static func dictionaryOf(assetBalance: AssetBalance) -> [String: Any?] { return [ "assetId": assetBalance.assetId, - "balance": assetBalance.balance, + "balanceSat": assetBalance.balanceSat, + "name": assetBalance.name == nil ? nil : assetBalance.name, + "ticker": assetBalance.ticker == nil ? nil : assetBalance.ticker, + "balance": assetBalance.balance == nil ? nil : assetBalance.balance, ] } @@ -143,6 +167,88 @@ enum BreezSDKLiquidMapper { return assetBalanceList.map { v -> [String: Any?] in return dictionaryOf(assetBalance: v) } } + static func asAssetInfo(assetInfo: [String: Any?]) throws -> AssetInfo { + guard let name = assetInfo["name"] as? String else { + throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "name", typeName: "AssetInfo")) + } + guard let ticker = assetInfo["ticker"] as? String else { + throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "ticker", typeName: "AssetInfo")) + } + guard let amount = assetInfo["amount"] as? Double else { + throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "amount", typeName: "AssetInfo")) + } + + return AssetInfo(name: name, ticker: ticker, amount: amount) + } + + static func dictionaryOf(assetInfo: AssetInfo) -> [String: Any?] { + return [ + "name": assetInfo.name, + "ticker": assetInfo.ticker, + "amount": assetInfo.amount, + ] + } + + static func asAssetInfoList(arr: [Any]) throws -> [AssetInfo] { + var list = [AssetInfo]() + for value in arr { + if let val = value as? [String: Any?] { + var assetInfo = try asAssetInfo(assetInfo: val) + list.append(assetInfo) + } else { + throw SdkError.Generic(message: errUnexpectedType(typeName: "AssetInfo")) + } + } + return list + } + + static func arrayOf(assetInfoList: [AssetInfo]) -> [Any] { + return assetInfoList.map { v -> [String: Any?] in return dictionaryOf(assetInfo: v) } + } + + static func asAssetMetadata(assetMetadata: [String: Any?]) throws -> AssetMetadata { + guard let assetId = assetMetadata["assetId"] as? String else { + throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "assetId", typeName: "AssetMetadata")) + } + guard let name = assetMetadata["name"] as? String else { + throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "name", typeName: "AssetMetadata")) + } + guard let ticker = assetMetadata["ticker"] as? String else { + throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "ticker", typeName: "AssetMetadata")) + } + guard let precision = assetMetadata["precision"] as? UInt8 else { + throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "precision", typeName: "AssetMetadata")) + } + + return AssetMetadata(assetId: assetId, name: name, ticker: ticker, precision: precision) + } + + static func dictionaryOf(assetMetadata: AssetMetadata) -> [String: Any?] { + return [ + "assetId": assetMetadata.assetId, + "name": assetMetadata.name, + "ticker": assetMetadata.ticker, + "precision": assetMetadata.precision, + ] + } + + static func asAssetMetadataList(arr: [Any]) throws -> [AssetMetadata] { + var list = [AssetMetadata]() + for value in arr { + if let val = value as? [String: Any?] { + var assetMetadata = try asAssetMetadata(assetMetadata: val) + list.append(assetMetadata) + } else { + throw SdkError.Generic(message: errUnexpectedType(typeName: "AssetMetadata")) + } + } + return list + } + + static func arrayOf(assetMetadataList: [AssetMetadata]) -> [Any] { + return assetMetadataList.map { v -> [String: Any?] in return dictionaryOf(assetMetadata: v) } + } + static func asBackupRequest(backupRequest: [String: Any?]) throws -> BackupRequest { var backupPath: String? if hasNonNilKey(data: backupRequest, key: "backupPath") { @@ -452,8 +558,12 @@ enum BreezSDKLiquidMapper { } onchainFeeRateLeewaySatPerVbyte = onchainFeeRateLeewaySatPerVbyteTmp } + var assetMetadata: [AssetMetadata]? + if let assetMetadataTmp = config["assetMetadata"] as? [[String: Any?]] { + assetMetadata = try asAssetMetadataList(arr: assetMetadataTmp) + } - return Config(liquidElectrumUrl: liquidElectrumUrl, bitcoinElectrumUrl: bitcoinElectrumUrl, mempoolspaceUrl: mempoolspaceUrl, workingDir: workingDir, network: network, paymentTimeoutSec: paymentTimeoutSec, zeroConfMinFeeRateMsat: zeroConfMinFeeRateMsat, syncServiceUrl: syncServiceUrl, breezApiKey: breezApiKey, cacheDir: cacheDir, zeroConfMaxAmountSat: zeroConfMaxAmountSat, useDefaultExternalInputParsers: useDefaultExternalInputParsers, externalInputParsers: externalInputParsers, onchainFeeRateLeewaySatPerVbyte: onchainFeeRateLeewaySatPerVbyte) + return Config(liquidElectrumUrl: liquidElectrumUrl, bitcoinElectrumUrl: bitcoinElectrumUrl, mempoolspaceUrl: mempoolspaceUrl, workingDir: workingDir, network: network, paymentTimeoutSec: paymentTimeoutSec, zeroConfMinFeeRateMsat: zeroConfMinFeeRateMsat, syncServiceUrl: syncServiceUrl, breezApiKey: breezApiKey, cacheDir: cacheDir, zeroConfMaxAmountSat: zeroConfMaxAmountSat, useDefaultExternalInputParsers: useDefaultExternalInputParsers, externalInputParsers: externalInputParsers, onchainFeeRateLeewaySatPerVbyte: onchainFeeRateLeewaySatPerVbyte, assetMetadata: assetMetadata) } static func dictionaryOf(config: Config) -> [String: Any?] { @@ -472,6 +582,7 @@ enum BreezSDKLiquidMapper { "useDefaultExternalInputParsers": config.useDefaultExternalInputParsers, "externalInputParsers": config.externalInputParsers == nil ? nil : arrayOf(externalInputParserList: config.externalInputParsers!), "onchainFeeRateLeewaySatPerVbyte": config.onchainFeeRateLeewaySatPerVbyte == nil ? nil : config.onchainFeeRateLeewaySatPerVbyte, + "assetMetadata": config.assetMetadata == nil ? nil : arrayOf(assetMetadataList: config.assetMetadata!), ] } @@ -1077,6 +1188,13 @@ enum BreezSDKLiquidMapper { } assetId = assetIdTmp } + var amount: Double? + if hasNonNilKey(data: liquidAddressData, key: "amount") { + guard let amountTmp = liquidAddressData["amount"] as? Double else { + throw SdkError.Generic(message: errUnexpectedValue(fieldName: "amount")) + } + amount = amountTmp + } var amountSat: UInt64? if hasNonNilKey(data: liquidAddressData, key: "amountSat") { guard let amountSatTmp = liquidAddressData["amountSat"] as? UInt64 else { @@ -1099,7 +1217,7 @@ enum BreezSDKLiquidMapper { message = messageTmp } - return LiquidAddressData(address: address, network: network, assetId: assetId, amountSat: amountSat, label: label, message: message) + return LiquidAddressData(address: address, network: network, assetId: assetId, amount: amount, amountSat: amountSat, label: label, message: message) } static func dictionaryOf(liquidAddressData: LiquidAddressData) -> [String: Any?] { @@ -1107,6 +1225,7 @@ enum BreezSDKLiquidMapper { "address": liquidAddressData.address, "network": valueOf(network: liquidAddressData.network), "assetId": liquidAddressData.assetId == nil ? nil : liquidAddressData.assetId, + "amount": liquidAddressData.amount == nil ? nil : liquidAddressData.amount, "amountSat": liquidAddressData.amountSat == nil ? nil : liquidAddressData.amountSat, "label": liquidAddressData.label == nil ? nil : liquidAddressData.label, "message": liquidAddressData.message == nil ? nil : liquidAddressData.message, @@ -3955,7 +4074,7 @@ enum BreezSDKLiquidMapper { guard let _assetId = payAmount["assetId"] as? String else { throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "assetId", typeName: "PayAmount")) } - guard let _receiverAmount = payAmount["receiverAmount"] as? UInt64 else { + guard let _receiverAmount = payAmount["receiverAmount"] as? Double else { throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "receiverAmount", typeName: "PayAmount")) } return PayAmount.asset(assetId: _assetId, receiverAmount: _receiverAmount) @@ -4053,7 +4172,12 @@ enum BreezSDKLiquidMapper { guard let _description = paymentDetails["description"] as? String else { throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "description", typeName: "PaymentDetails")) } - return PaymentDetails.liquid(assetId: _assetId, destination: _destination, description: _description) + var _assetInfo: AssetInfo? + if let assetInfoTmp = paymentDetails["assetInfo"] as? [String: Any?] { + _assetInfo = try asAssetInfo(assetInfo: assetInfoTmp) + } + + return PaymentDetails.liquid(assetId: _assetId, destination: _destination, description: _description, assetInfo: _assetInfo) } if type == "bitcoin" { guard let _swapId = paymentDetails["swapId"] as? String else { @@ -4100,13 +4224,14 @@ enum BreezSDKLiquidMapper { ] case let .liquid( - assetId, destination, description + assetId, destination, description, assetInfo ): return [ "type": "liquid", "assetId": assetId, "destination": destination, "description": description, + "assetInfo": assetInfo == nil ? nil : dictionaryOf(assetInfo: assetInfo!), ] case let .bitcoin( @@ -4313,7 +4438,7 @@ enum BreezSDKLiquidMapper { guard let _assetId = receiveAmount["assetId"] as? String else { throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "assetId", typeName: "ReceiveAmount")) } - let _payerAmount = receiveAmount["payerAmount"] as? UInt64 + let _payerAmount = receiveAmount["payerAmount"] as? Double return ReceiveAmount.asset(assetId: _assetId, payerAmount: _payerAmount) } diff --git a/packages/react-native/src/index.ts b/packages/react-native/src/index.ts index 24edc7879..0ae384ca4 100644 --- a/packages/react-native/src/index.ts +++ b/packages/react-native/src/index.ts @@ -36,7 +36,23 @@ export interface AesSuccessActionDataDecrypted { export interface AssetBalance { assetId: string - balance: number + balanceSat: number + name?: string + ticker?: string + balance?: number +} + +export interface AssetInfo { + name: string + ticker: string + amount: number +} + +export interface AssetMetadata { + assetId: string + name: string + ticker: string + precision: number } export interface BackupRequest { @@ -86,6 +102,7 @@ export interface Config { useDefaultExternalInputParsers: boolean externalInputParsers?: ExternalInputParser[] onchainFeeRateLeewaySatPerVbyte?: number + assetMetadata?: AssetMetadata[] } export interface ConnectRequest { @@ -175,6 +192,7 @@ export interface LiquidAddressData { address: string network: Network assetId?: string + amount?: number amountSat?: number label?: string message?: string @@ -670,6 +688,7 @@ export type PaymentDetails = { assetId: string destination: string description: string + assetInfo?: AssetInfo } | { type: PaymentDetailsVariant.BITCOIN, swapId: string