mirror of
https://github.com/aljazceru/breez-sdk-liquid.git
synced 2025-12-17 22:14:24 +01:00
Add Config (#267)
* Add config * Add rustdocs to Config, send_payment (#271) --------- Co-authored-by: ok300 <106775972+ok300@users.noreply.github.com>
This commit is contained in:
@@ -74,10 +74,11 @@ async fn main() -> Result<()> {
|
|||||||
|
|
||||||
let mnemonic = persistence.get_or_create_mnemonic()?;
|
let mnemonic = persistence.get_or_create_mnemonic()?;
|
||||||
let network = args.network.unwrap_or(Network::Testnet);
|
let network = args.network.unwrap_or(Network::Testnet);
|
||||||
|
let mut config = LiquidSdk::default_config(network);
|
||||||
|
config.working_dir = data_dir_str;
|
||||||
let sdk = LiquidSdk::connect(ConnectRequest {
|
let sdk = LiquidSdk::connect(ConnectRequest {
|
||||||
mnemonic: mnemonic.to_string(),
|
mnemonic: mnemonic.to_string(),
|
||||||
data_dir: Some(data_dir_str),
|
config,
|
||||||
network,
|
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
let listener_id = sdk
|
let listener_id = sdk
|
||||||
|
|||||||
@@ -55,10 +55,17 @@ typedef struct wire_cst_prepare_send_response {
|
|||||||
uint64_t fees_sat;
|
uint64_t fees_sat;
|
||||||
} wire_cst_prepare_send_response;
|
} wire_cst_prepare_send_response;
|
||||||
|
|
||||||
|
typedef struct wire_cst_config {
|
||||||
|
struct wire_cst_list_prim_u_8_strict *boltz_url;
|
||||||
|
struct wire_cst_list_prim_u_8_strict *electrum_url;
|
||||||
|
struct wire_cst_list_prim_u_8_strict *working_dir;
|
||||||
|
int32_t network;
|
||||||
|
uint64_t payment_timeout_sec;
|
||||||
|
} wire_cst_config;
|
||||||
|
|
||||||
typedef struct wire_cst_connect_request {
|
typedef struct wire_cst_connect_request {
|
||||||
struct wire_cst_list_prim_u_8_strict *mnemonic;
|
struct wire_cst_list_prim_u_8_strict *mnemonic;
|
||||||
struct wire_cst_list_prim_u_8_strict *data_dir;
|
struct wire_cst_config config;
|
||||||
int32_t network;
|
|
||||||
} wire_cst_connect_request;
|
} wire_cst_connect_request;
|
||||||
|
|
||||||
typedef struct wire_cst_payment {
|
typedef struct wire_cst_payment {
|
||||||
@@ -271,6 +278,8 @@ void frbgen_breez_liquid_wire__crate__bindings__breez_log_stream(int64_t port_,
|
|||||||
void frbgen_breez_liquid_wire__crate__bindings__connect(int64_t port_,
|
void frbgen_breez_liquid_wire__crate__bindings__connect(int64_t port_,
|
||||||
struct wire_cst_connect_request *req);
|
struct wire_cst_connect_request *req);
|
||||||
|
|
||||||
|
void frbgen_breez_liquid_wire__crate__bindings__default_config(int64_t port_, int32_t network);
|
||||||
|
|
||||||
void frbgen_breez_liquid_wire__crate__bindings__parse_invoice(int64_t port_,
|
void frbgen_breez_liquid_wire__crate__bindings__parse_invoice(int64_t port_,
|
||||||
struct wire_cst_list_prim_u_8_strict *input);
|
struct wire_cst_list_prim_u_8_strict *input);
|
||||||
|
|
||||||
@@ -337,6 +346,7 @@ static int64_t dummy_method_to_enforce_bundling(void) {
|
|||||||
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_sync);
|
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_sync);
|
||||||
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__breez_log_stream);
|
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__breez_log_stream);
|
||||||
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__connect);
|
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__connect);
|
||||||
|
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__default_config);
|
||||||
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__parse_invoice);
|
dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__parse_invoice);
|
||||||
dummy_var ^= ((int64_t) (void*) store_dart_post_cobject);
|
dummy_var ^= ((int64_t) (void*) store_dart_post_cobject);
|
||||||
return dummy_var;
|
return dummy_var;
|
||||||
|
|||||||
@@ -22,6 +22,11 @@
|
|||||||
{%- match func.return_type() -%}
|
{%- match func.return_type() -%}
|
||||||
{%- when Some with (return_type) %}
|
{%- when Some with (return_type) %}
|
||||||
val res = {{ obj_interface }}{{ func.name()|fn_name|unquote }}({%- call kt::arg_list(func) -%})
|
val res = {{ obj_interface }}{{ func.name()|fn_name|unquote }}({%- call kt::arg_list(func) -%})
|
||||||
|
{%- if func.name() == "default_config" %}
|
||||||
|
val workingDir = File(reactApplicationContext.filesDir.toString() + "/breezLiquidSdk")
|
||||||
|
|
||||||
|
res.workingDir = workingDir.absolutePath
|
||||||
|
{%- endif -%}
|
||||||
{%- match return_type %}
|
{%- match return_type %}
|
||||||
{%- when Type::Optional(inner) %}
|
{%- when Type::Optional(inner) %}
|
||||||
{%- let unboxed = inner.as_ref() %}
|
{%- let unboxed = inner.as_ref() %}
|
||||||
|
|||||||
@@ -36,6 +36,19 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext
|
|||||||
throw LiquidSdkException.Generic("Not initialized")
|
throw LiquidSdkException.Generic("Not initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throws(LiquidSdkException::class)
|
||||||
|
private fun ensureWorkingDir(workingDir: String) {
|
||||||
|
try {
|
||||||
|
val workingDirFile = File(workingDir)
|
||||||
|
|
||||||
|
if (!workingDirFile.exists() && !workingDirFile.mkdirs()) {
|
||||||
|
throw LiquidSdkException.Generic("Mandatory field workingDir must contain a writable directory")
|
||||||
|
}
|
||||||
|
} catch (e: SecurityException) {
|
||||||
|
throw LiquidSdkException.Generic("Mandatory field workingDir must contain a writable directory")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
fun addListener(eventName: String) {}
|
fun addListener(eventName: String) {}
|
||||||
|
|
||||||
@@ -73,7 +86,9 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext
|
|||||||
executor.execute {
|
executor.execute {
|
||||||
try {
|
try {
|
||||||
var connectRequest = asConnectRequest(req) ?: run { throw LiquidSdkException.Generic(errMissingMandatoryField("req", "ConnectRequest")) }
|
var connectRequest = asConnectRequest(req) ?: run { throw LiquidSdkException.Generic(errMissingMandatoryField("req", "ConnectRequest")) }
|
||||||
connectRequest.dataDir = connectRequest.dataDir?.takeUnless { it.isEmpty() } ?: run { reactApplicationContext.filesDir.toString() + "/breezLiquidSdk" }
|
|
||||||
|
ensureWorkingDir(connectRequest.config.workingDir)
|
||||||
|
|
||||||
bindingLiquidSdk = connect(connectRequest)
|
bindingLiquidSdk = connect(connectRequest)
|
||||||
promise.resolve(readableMapOf("status" to "ok"))
|
promise.resolve(readableMapOf("status" to "ok"))
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|||||||
@@ -21,6 +21,9 @@
|
|||||||
{%- match func.return_type() -%}
|
{%- match func.return_type() -%}
|
||||||
{%- when Some with (return_type) %}
|
{%- when Some with (return_type) %}
|
||||||
var res = {%- call swift::throws_decl(func) -%}{{ obj_interface }}{{ func.name()|fn_name|unquote }}({%- call swift::arg_list(func) -%})
|
var res = {%- call swift::throws_decl(func) -%}{{ obj_interface }}{{ func.name()|fn_name|unquote }}({%- call swift::arg_list(func) -%})
|
||||||
|
{%- if func.name() == "default_config" %}
|
||||||
|
res.workingDir = RNBreezLiquidSDK.breezLiquidSdkDirectory.path
|
||||||
|
{%- endif -%}
|
||||||
{%- match return_type %}
|
{%- match return_type %}
|
||||||
{%- when Type::Optional(inner) %}
|
{%- when Type::Optional(inner) %}
|
||||||
{%- let unboxed = inner.as_ref() %}
|
{%- let unboxed = inner.as_ref() %}
|
||||||
|
|||||||
@@ -11,10 +11,16 @@ class RNBreezLiquidSDK: RCTEventEmitter {
|
|||||||
|
|
||||||
private var bindingLiquidSdk: BindingLiquidSdk!
|
private var bindingLiquidSdk: BindingLiquidSdk!
|
||||||
|
|
||||||
static var defaultDataDir: URL {
|
|
||||||
|
static var breezLiquidSdkDirectory: URL {
|
||||||
let applicationDirectory = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!
|
let applicationDirectory = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!
|
||||||
|
let breezLiquidSdkDirectory = applicationDirectory.appendingPathComponent("breezLiquidSdk", isDirectory: true)
|
||||||
|
|
||||||
return applicationDirectory.appendingPathComponent("breezLiquidSdk", isDirectory: true)
|
if !FileManager.default.fileExists(atPath: breezLiquidSdkDirectory.path) {
|
||||||
|
try! FileManager.default.createDirectory(atPath: breezLiquidSdkDirectory.path, withIntermediateDirectories: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return breezLiquidSdkDirectory
|
||||||
}
|
}
|
||||||
|
|
||||||
override init() {
|
override init() {
|
||||||
@@ -55,7 +61,17 @@ class RNBreezLiquidSDK: RCTEventEmitter {
|
|||||||
|
|
||||||
throw LiquidSdkError.Generic(message: "Not initialized")
|
throw LiquidSdkError.Generic(message: "Not initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func ensureWorkingDir(workingDir: String) throws {
|
||||||
|
do {
|
||||||
|
if !FileManager.default.fileExists(atPath: workingDir) {
|
||||||
|
try FileManager.default.createDirectory(atPath: workingDir, withIntermediateDirectories: true)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
throw LiquidSdkError.Generic(message: "Mandatory field workingDir must contain a writable directory")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{% let obj_interface = "BreezLiquidSDK." -%}
|
{% let obj_interface = "BreezLiquidSDK." -%}
|
||||||
{% for func in ci.function_definitions() %}
|
{% for func in ci.function_definitions() %}
|
||||||
{%- if func.name()|ignored_function == false -%}
|
{%- if func.name()|ignored_function == false -%}
|
||||||
@@ -81,7 +97,8 @@ class RNBreezLiquidSDK: RCTEventEmitter {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
var connectRequest = try BreezLiquidSDKMapper.asConnectRequest(connectRequest: req)
|
var connectRequest = try BreezLiquidSDKMapper.asConnectRequest(connectRequest: req)
|
||||||
connectRequest.dataDir = connectRequest.dataDir == nil || connectRequest.dataDir!.isEmpty ? RNBreezLiquidSDK.defaultDataDir.path : connectRequest.dataDir
|
try ensureWorkingDir(workingDir: connectRequest.config.workingDir)
|
||||||
|
|
||||||
bindingLiquidSdk = try BreezLiquidSDK.connect(req: connectRequest)
|
bindingLiquidSdk = try BreezLiquidSDK.connect(req: connectRequest)
|
||||||
resolve(["status": "ok"])
|
resolve(["status": "ok"])
|
||||||
} catch let err {
|
} catch let err {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { NativeModules, Platform, NativeEventEmitter } from "react-native"
|
import { NativeModules, Platform, EmitterSubscription, NativeEventEmitter } from "react-native"
|
||||||
|
|
||||||
const LINKING_ERROR =
|
const LINKING_ERROR =
|
||||||
`The package 'react-native-breez-liquid-sdk' doesn't seem to be linked. Make sure: \n\n` +
|
`The package 'react-native-breez-liquid-sdk' doesn't seem to be linked. Make sure: \n\n` +
|
||||||
@@ -20,7 +20,7 @@ const BreezLiquidSDK = NativeModules.RNBreezLiquidSDK
|
|||||||
const BreezLiquidSDKEmitter = new NativeEventEmitter(BreezLiquidSDK)
|
const BreezLiquidSDKEmitter = new NativeEventEmitter(BreezLiquidSDK)
|
||||||
{%- import "macros.ts" as ts %}
|
{%- import "macros.ts" as ts %}
|
||||||
{%- include "Types.ts" %}
|
{%- include "Types.ts" %}
|
||||||
{% include "Helpers.ts" -%}
|
{% include "Helpers.ts" %}
|
||||||
{% for func in ci.function_definitions() %}
|
{% for func in ci.function_definitions() %}
|
||||||
{%- if func.name()|ignored_function == false -%}
|
{%- if func.name()|ignored_function == false -%}
|
||||||
{%- include "TopLevelFunctionTemplate.ts" %}
|
{%- include "TopLevelFunctionTemplate.ts" %}
|
||||||
|
|||||||
@@ -23,15 +23,22 @@ enum PaymentError {
|
|||||||
"SignerError",
|
"SignerError",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
dictionary Config {
|
||||||
|
string boltz_url;
|
||||||
|
string electrum_url;
|
||||||
|
string working_dir;
|
||||||
|
Network network;
|
||||||
|
u64 payment_timeout_sec;
|
||||||
|
};
|
||||||
|
|
||||||
enum Network {
|
enum Network {
|
||||||
"Mainnet",
|
"Mainnet",
|
||||||
"Testnet",
|
"Testnet",
|
||||||
};
|
};
|
||||||
|
|
||||||
dictionary ConnectRequest {
|
dictionary ConnectRequest {
|
||||||
|
Config config;
|
||||||
string mnemonic;
|
string mnemonic;
|
||||||
Network network;
|
|
||||||
string? data_dir = null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
dictionary GetInfoRequest {
|
dictionary GetInfoRequest {
|
||||||
@@ -165,6 +172,8 @@ namespace breez_liquid_sdk {
|
|||||||
|
|
||||||
[Throws=LiquidSdkError]
|
[Throws=LiquidSdkError]
|
||||||
void set_logger(Logger logger);
|
void set_logger(Logger logger);
|
||||||
|
|
||||||
|
Config default_config(Network network);
|
||||||
|
|
||||||
[Throws=PaymentError]
|
[Throws=PaymentError]
|
||||||
LNInvoice parse_invoice(string invoice);
|
LNInvoice parse_invoice(string invoice);
|
||||||
|
|||||||
@@ -58,6 +58,10 @@ pub fn connect(req: ConnectRequest) -> Result<Arc<BindingLiquidSdk>, LiquidSdkEr
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn default_config(network: Network) -> Config {
|
||||||
|
LiquidSdk::default_config(network)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_invoice(input: String) -> Result<LNInvoice, PaymentError> {
|
pub fn parse_invoice(input: String) -> Result<LNInvoice, PaymentError> {
|
||||||
LiquidSdk::parse_invoice(&input)
|
LiquidSdk::parse_invoice(&input)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ class SDKListener: breez_liquid_sdk.EventListener {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
var mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
|
var mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
|
||||||
|
var config = breez_liquid_sdk.defaultConfig(breez_liquid_sdk.Network.TESTNET)
|
||||||
var connectReq = breez_liquid_sdk.ConnectRequest(mnemonic, breez_liquid_sdk.Network.LIQUID_TESTNET)
|
var connectRequest = breez_liquid_sdk.ConnectRequest(config, mnemonic)
|
||||||
var sdk = breez_liquid_sdk.connect(connectReq)
|
var sdk = breez_liquid_sdk.connect(connectRequest)
|
||||||
|
|
||||||
var listenerId = sdk.addEventListener(SDKListener())
|
var listenerId = sdk.addEventListener(SDKListener())
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ class SDKListener(breez_liquid_sdk.EventListener):
|
|||||||
|
|
||||||
def test():
|
def test():
|
||||||
mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
|
mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
|
||||||
|
config = breez_liquid_sdk.default_config(breez_liquid_sdk.Network.TESTNET)
|
||||||
connect_req = breez_liquid_sdk.ConnectRequest(mnemonic=mnemonic, network=breez_liquid_sdk.Network.LIQUID_TESTNET)
|
connect_request = breez_liquid_sdk.ConnectRequest(config=config, mnemonic=mnemonic)
|
||||||
sdk = breez_liquid_sdk.connect(connect_req)
|
sdk = breez_liquid_sdk.connect(connect_request)
|
||||||
|
|
||||||
listener_id = sdk.add_event_listener(SDKListener())
|
listener_id = sdk.add_event_listener(SDKListener())
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ class SDKListener: EventListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
|
let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
|
||||||
|
let config = breez_liquid_sdk.defaultConfig(network: .testnet);
|
||||||
let connectReq = breez_liquid_sdk.ConnectRequest(mnemonic: mnemonic, network: .liquidTestnet);
|
let connectRequest = breez_liquid_sdk.ConnectRequest(config: config, mnemonic: mnemonic);
|
||||||
let sdk = try breez_liquid_sdk.connect(req: connectReq);
|
let sdk = try breez_liquid_sdk.connect(req: connectRequest);
|
||||||
|
|
||||||
let listenerId = try sdk.addEventListener(listener: SDKListener());
|
let listenerId = try sdk.addEventListener(listener: SDKListener());
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,10 @@ pub fn breez_log_stream(s: StreamSink<LogEntry>) -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn default_config(network: Network) -> Config {
|
||||||
|
LiquidSdk::default_config(network)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_invoice(input: String) -> Result<LNInvoice, PaymentError> {
|
pub fn parse_invoice(input: String) -> Result<LNInvoice, PaymentError> {
|
||||||
LiquidSdk::parse_invoice(&input)
|
LiquidSdk::parse_invoice(&input)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,13 +157,24 @@ impl CstDecode<u64> for *mut u64 {
|
|||||||
unsafe { *flutter_rust_bridge::for_generated::box_from_leak_ptr(self) }
|
unsafe { *flutter_rust_bridge::for_generated::box_from_leak_ptr(self) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl CstDecode<crate::model::Config> for wire_cst_config {
|
||||||
|
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||||
|
fn cst_decode(self) -> crate::model::Config {
|
||||||
|
crate::model::Config {
|
||||||
|
boltz_url: self.boltz_url.cst_decode(),
|
||||||
|
electrum_url: self.electrum_url.cst_decode(),
|
||||||
|
working_dir: self.working_dir.cst_decode(),
|
||||||
|
network: self.network.cst_decode(),
|
||||||
|
payment_timeout_sec: self.payment_timeout_sec.cst_decode(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
impl CstDecode<crate::model::ConnectRequest> for wire_cst_connect_request {
|
impl CstDecode<crate::model::ConnectRequest> for wire_cst_connect_request {
|
||||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||||
fn cst_decode(self) -> crate::model::ConnectRequest {
|
fn cst_decode(self) -> crate::model::ConnectRequest {
|
||||||
crate::model::ConnectRequest {
|
crate::model::ConnectRequest {
|
||||||
mnemonic: self.mnemonic.cst_decode(),
|
mnemonic: self.mnemonic.cst_decode(),
|
||||||
data_dir: self.data_dir.cst_decode(),
|
config: self.config.cst_decode(),
|
||||||
network: self.network.cst_decode(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -472,12 +483,27 @@ impl Default for wire_cst_backup_request {
|
|||||||
Self::new_with_null_ptr()
|
Self::new_with_null_ptr()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl NewWithNullPtr for wire_cst_config {
|
||||||
|
fn new_with_null_ptr() -> Self {
|
||||||
|
Self {
|
||||||
|
boltz_url: core::ptr::null_mut(),
|
||||||
|
electrum_url: core::ptr::null_mut(),
|
||||||
|
working_dir: core::ptr::null_mut(),
|
||||||
|
network: Default::default(),
|
||||||
|
payment_timeout_sec: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Default for wire_cst_config {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new_with_null_ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
impl NewWithNullPtr for wire_cst_connect_request {
|
impl NewWithNullPtr for wire_cst_connect_request {
|
||||||
fn new_with_null_ptr() -> Self {
|
fn new_with_null_ptr() -> Self {
|
||||||
Self {
|
Self {
|
||||||
mnemonic: core::ptr::null_mut(),
|
mnemonic: core::ptr::null_mut(),
|
||||||
data_dir: core::ptr::null_mut(),
|
config: Default::default(),
|
||||||
network: Default::default(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -844,6 +870,14 @@ pub extern "C" fn frbgen_breez_liquid_wire__crate__bindings__connect(
|
|||||||
wire__crate__bindings__connect_impl(port_, req)
|
wire__crate__bindings__connect_impl(port_, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn frbgen_breez_liquid_wire__crate__bindings__default_config(
|
||||||
|
port_: i64,
|
||||||
|
network: i32,
|
||||||
|
) {
|
||||||
|
wire__crate__bindings__default_config_impl(port_, network)
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn frbgen_breez_liquid_wire__crate__bindings__parse_invoice(
|
pub extern "C" fn frbgen_breez_liquid_wire__crate__bindings__parse_invoice(
|
||||||
port_: i64,
|
port_: i64,
|
||||||
@@ -1002,10 +1036,18 @@ pub struct wire_cst_backup_request {
|
|||||||
}
|
}
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct wire_cst_config {
|
||||||
|
boltz_url: *mut wire_cst_list_prim_u_8_strict,
|
||||||
|
electrum_url: *mut wire_cst_list_prim_u_8_strict,
|
||||||
|
working_dir: *mut wire_cst_list_prim_u_8_strict,
|
||||||
|
network: i32,
|
||||||
|
payment_timeout_sec: u64,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
pub struct wire_cst_connect_request {
|
pub struct wire_cst_connect_request {
|
||||||
mnemonic: *mut wire_cst_list_prim_u_8_strict,
|
mnemonic: *mut wire_cst_list_prim_u_8_strict,
|
||||||
data_dir: *mut wire_cst_list_prim_u_8_strict,
|
config: wire_cst_config,
|
||||||
network: i32,
|
|
||||||
}
|
}
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ flutter_rust_bridge::frb_generated_boilerplate!(
|
|||||||
default_rust_auto_opaque = RustAutoOpaqueNom,
|
default_rust_auto_opaque = RustAutoOpaqueNom,
|
||||||
);
|
);
|
||||||
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.0.0-dev.36";
|
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.0.0-dev.36";
|
||||||
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -532134055;
|
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 1028270774;
|
||||||
|
|
||||||
// Section: executor
|
// Section: executor
|
||||||
|
|
||||||
@@ -390,6 +390,26 @@ fn wire__crate__bindings__connect_impl(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
fn wire__crate__bindings__default_config_impl(
|
||||||
|
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||||
|
network: impl CstDecode<crate::model::Network>,
|
||||||
|
) {
|
||||||
|
FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::<flutter_rust_bridge::for_generated::DcoCodec, _, _>(
|
||||||
|
flutter_rust_bridge::for_generated::TaskInfo {
|
||||||
|
debug_name: "default_config",
|
||||||
|
port: Some(port_),
|
||||||
|
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
|
||||||
|
},
|
||||||
|
move || {
|
||||||
|
let api_network = network.cst_decode();
|
||||||
|
move |context| {
|
||||||
|
transform_result_dco((move || {
|
||||||
|
Result::<_, ()>::Ok(crate::bindings::default_config(api_network))
|
||||||
|
})())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
fn wire__crate__bindings__parse_invoice_impl(
|
fn wire__crate__bindings__parse_invoice_impl(
|
||||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||||
input: impl CstDecode<String>,
|
input: impl CstDecode<String>,
|
||||||
@@ -553,16 +573,32 @@ impl SseDecode for bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SseDecode for crate::model::Config {
|
||||||
|
// 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_boltzUrl = <String>::sse_decode(deserializer);
|
||||||
|
let mut var_electrumUrl = <String>::sse_decode(deserializer);
|
||||||
|
let mut var_workingDir = <String>::sse_decode(deserializer);
|
||||||
|
let mut var_network = <crate::model::Network>::sse_decode(deserializer);
|
||||||
|
let mut var_paymentTimeoutSec = <u64>::sse_decode(deserializer);
|
||||||
|
return crate::model::Config {
|
||||||
|
boltz_url: var_boltzUrl,
|
||||||
|
electrum_url: var_electrumUrl,
|
||||||
|
working_dir: var_workingDir,
|
||||||
|
network: var_network,
|
||||||
|
payment_timeout_sec: var_paymentTimeoutSec,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl SseDecode for crate::model::ConnectRequest {
|
impl SseDecode for crate::model::ConnectRequest {
|
||||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
|
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
|
||||||
let mut var_mnemonic = <String>::sse_decode(deserializer);
|
let mut var_mnemonic = <String>::sse_decode(deserializer);
|
||||||
let mut var_dataDir = <Option<String>>::sse_decode(deserializer);
|
let mut var_config = <crate::model::Config>::sse_decode(deserializer);
|
||||||
let mut var_network = <crate::model::Network>::sse_decode(deserializer);
|
|
||||||
return crate::model::ConnectRequest {
|
return crate::model::ConnectRequest {
|
||||||
mnemonic: var_mnemonic,
|
mnemonic: var_mnemonic,
|
||||||
data_dir: var_dataDir,
|
config: var_config,
|
||||||
network: var_network,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1113,12 +1149,30 @@ impl flutter_rust_bridge::IntoIntoDart<crate::model::BackupRequest>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||||
|
impl flutter_rust_bridge::IntoDart for crate::model::Config {
|
||||||
|
fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi {
|
||||||
|
[
|
||||||
|
self.boltz_url.into_into_dart().into_dart(),
|
||||||
|
self.electrum_url.into_into_dart().into_dart(),
|
||||||
|
self.working_dir.into_into_dart().into_dart(),
|
||||||
|
self.network.into_into_dart().into_dart(),
|
||||||
|
self.payment_timeout_sec.into_into_dart().into_dart(),
|
||||||
|
]
|
||||||
|
.into_dart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive for crate::model::Config {}
|
||||||
|
impl flutter_rust_bridge::IntoIntoDart<crate::model::Config> for crate::model::Config {
|
||||||
|
fn into_into_dart(self) -> crate::model::Config {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||||
impl flutter_rust_bridge::IntoDart for crate::model::ConnectRequest {
|
impl flutter_rust_bridge::IntoDart for crate::model::ConnectRequest {
|
||||||
fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi {
|
fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi {
|
||||||
[
|
[
|
||||||
self.mnemonic.into_into_dart().into_dart(),
|
self.mnemonic.into_into_dart().into_dart(),
|
||||||
self.data_dir.into_into_dart().into_dart(),
|
self.config.into_into_dart().into_dart(),
|
||||||
self.network.into_into_dart().into_dart(),
|
|
||||||
]
|
]
|
||||||
.into_dart()
|
.into_dart()
|
||||||
}
|
}
|
||||||
@@ -1603,12 +1657,22 @@ impl SseEncode for bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SseEncode for crate::model::Config {
|
||||||
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
|
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
|
||||||
|
<String>::sse_encode(self.boltz_url, serializer);
|
||||||
|
<String>::sse_encode(self.electrum_url, serializer);
|
||||||
|
<String>::sse_encode(self.working_dir, serializer);
|
||||||
|
<crate::model::Network>::sse_encode(self.network, serializer);
|
||||||
|
<u64>::sse_encode(self.payment_timeout_sec, serializer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl SseEncode for crate::model::ConnectRequest {
|
impl SseEncode for crate::model::ConnectRequest {
|
||||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
|
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
|
||||||
<String>::sse_encode(self.mnemonic, serializer);
|
<String>::sse_encode(self.mnemonic, serializer);
|
||||||
<Option<String>>::sse_encode(self.data_dir, serializer);
|
<crate::model::Config>::sse_encode(self.config, serializer);
|
||||||
<crate::model::Network>::sse_encode(self.network, serializer);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
|
use boltz_client::network::electrum::ElectrumConfig;
|
||||||
use boltz_client::network::Chain;
|
use boltz_client::network::Chain;
|
||||||
use boltz_client::swaps::boltzv2::{
|
use boltz_client::swaps::boltzv2::{
|
||||||
CreateReverseResponse, CreateSubmarineResponse, Leaf, SwapTree,
|
CreateReverseResponse, CreateSubmarineResponse, Leaf, SwapTree, BOLTZ_MAINNET_URL_V2,
|
||||||
|
BOLTZ_TESTNET_URL_V2,
|
||||||
};
|
};
|
||||||
use boltz_client::{Keypair, LBtcSwapScriptV2, ToHex};
|
use boltz_client::{Keypair, LBtcSwapScriptV2, ToHex};
|
||||||
use lwk_signer::SwSigner;
|
use lwk_signer::SwSigner;
|
||||||
use lwk_wollet::{ElectrumUrl, ElementsNetwork, WolletDescriptor};
|
use lwk_wollet::{ElectrumClient, ElectrumUrl, ElementsNetwork, WolletDescriptor};
|
||||||
use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSqlOutput, ValueRef};
|
use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSqlOutput, ValueRef};
|
||||||
use rusqlite::ToSql;
|
use rusqlite::ToSql;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -13,6 +15,49 @@ use serde::{Deserialize, Serialize};
|
|||||||
use crate::error::PaymentError;
|
use crate::error::PaymentError;
|
||||||
use crate::utils;
|
use crate::utils;
|
||||||
|
|
||||||
|
/// Configuration for the Liquid SDK
|
||||||
|
#[derive(Clone, Debug, Serialize)]
|
||||||
|
pub struct Config {
|
||||||
|
pub boltz_url: String,
|
||||||
|
pub electrum_url: String,
|
||||||
|
/// Directory in which all SDK files (DB, log, cache) are stored.
|
||||||
|
///
|
||||||
|
/// Prefix can be a relative or absolute path to this directory.
|
||||||
|
pub working_dir: String,
|
||||||
|
pub network: Network,
|
||||||
|
/// Send payment timeout. See [crate::sdk::LiquidSdk::send_payment]
|
||||||
|
pub payment_timeout_sec: u64,
|
||||||
|
}
|
||||||
|
impl Config {
|
||||||
|
pub(crate) fn get_electrum_client(&self) -> Result<ElectrumClient, lwk_wollet::Error> {
|
||||||
|
ElectrumClient::new(&ElectrumUrl::new(&self.electrum_url, true, true))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_electrum_config(&self) -> ElectrumConfig {
|
||||||
|
ElectrumConfig::new(self.network.into(), &self.electrum_url, true, true, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mainnet() -> Self {
|
||||||
|
Config {
|
||||||
|
boltz_url: BOLTZ_MAINNET_URL_V2.to_owned(),
|
||||||
|
electrum_url: "blockstream.info:995".to_string(),
|
||||||
|
working_dir: ".".to_string(),
|
||||||
|
network: Network::Mainnet,
|
||||||
|
payment_timeout_sec: 15,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn testnet() -> Self {
|
||||||
|
Config {
|
||||||
|
boltz_url: BOLTZ_TESTNET_URL_V2.to_owned(),
|
||||||
|
electrum_url: "blockstream.info:465".to_string(),
|
||||||
|
working_dir: ".".to_string(),
|
||||||
|
network: Network::Testnet,
|
||||||
|
payment_timeout_sec: 15,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Serialize)]
|
#[derive(Debug, Copy, Clone, PartialEq, Serialize)]
|
||||||
pub enum Network {
|
pub enum Network {
|
||||||
/// Mainnet Bitcoin and Liquid chains
|
/// Mainnet Bitcoin and Liquid chains
|
||||||
@@ -84,39 +129,18 @@ pub enum LiquidSdkEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct LiquidSdkOptions {
|
pub struct LiquidSdkOptions {
|
||||||
|
pub config: Config,
|
||||||
pub signer: SwSigner,
|
pub signer: SwSigner,
|
||||||
pub network: Network,
|
|
||||||
/// Output script descriptor
|
/// Output script descriptor
|
||||||
///
|
///
|
||||||
/// See <https://github.com/bitcoin/bips/pull/1143>
|
/// See <https://github.com/bitcoin/bips/pull/1143>
|
||||||
pub descriptor: WolletDescriptor,
|
pub descriptor: WolletDescriptor,
|
||||||
/// Absolute or relative path to the data dir, including the dir name.
|
|
||||||
///
|
|
||||||
/// If not set, it defaults to [crate::DEFAULT_DATA_DIR].
|
|
||||||
pub data_dir_path: Option<String>,
|
|
||||||
/// Custom Electrum URL. If set, it must match the specified network.
|
|
||||||
///
|
|
||||||
/// If not set, it defaults to a Blockstream instance.
|
|
||||||
pub electrum_url: Option<ElectrumUrl>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LiquidSdkOptions {
|
|
||||||
pub(crate) fn get_electrum_url(&self) -> ElectrumUrl {
|
|
||||||
self.electrum_url.clone().unwrap_or({
|
|
||||||
let (url, validate_domain, tls) = match &self.network {
|
|
||||||
Network::Mainnet => ("blockstream.info:995", true, true),
|
|
||||||
Network::Testnet => ("blockstream.info:465", true, true),
|
|
||||||
};
|
|
||||||
ElectrumUrl::new(url, tls, validate_domain)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
pub struct ConnectRequest {
|
pub struct ConnectRequest {
|
||||||
pub mnemonic: String,
|
pub mnemonic: String,
|
||||||
pub data_dir: Option<String>,
|
pub config: Config,
|
||||||
pub network: Network,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ use boltz_client::network::Chain;
|
|||||||
use boltz_client::swaps::boltzv2;
|
use boltz_client::swaps::boltzv2;
|
||||||
use boltz_client::ToHex;
|
use boltz_client::ToHex;
|
||||||
use boltz_client::{
|
use boltz_client::{
|
||||||
network::electrum::ElectrumConfig,
|
|
||||||
swaps::{
|
swaps::{
|
||||||
boltz::{RevSwapStates, SubSwapStates},
|
boltz::{RevSwapStates, SubSwapStates},
|
||||||
boltzv2::*,
|
boltzv2::*,
|
||||||
@@ -30,8 +29,7 @@ use lwk_wollet::elements::LockTime;
|
|||||||
use lwk_wollet::hashes::{sha256, Hash};
|
use lwk_wollet::hashes::{sha256, Hash};
|
||||||
use lwk_wollet::{
|
use lwk_wollet::{
|
||||||
elements::{Address, Transaction},
|
elements::{Address, Transaction},
|
||||||
BlockchainBackend, ElectrumClient, ElectrumUrl, ElementsNetwork, FsPersister,
|
BlockchainBackend, ElementsNetwork, FsPersister, Wollet as LwkWollet, WolletDescriptor,
|
||||||
Wollet as LwkWollet, WolletDescriptor,
|
|
||||||
};
|
};
|
||||||
use tokio::sync::{watch, Mutex, RwLock};
|
use tokio::sync::{watch, Mutex, RwLock};
|
||||||
use tokio::time::MissedTickBehavior;
|
use tokio::time::MissedTickBehavior;
|
||||||
@@ -56,14 +54,12 @@ pub const LIQUID_CLAIM_TX_FEERATE_MSAT: f32 = 100.0;
|
|||||||
pub const DEFAULT_DATA_DIR: &str = ".data";
|
pub const DEFAULT_DATA_DIR: &str = ".data";
|
||||||
|
|
||||||
pub struct LiquidSdk {
|
pub struct LiquidSdk {
|
||||||
electrum_url: ElectrumUrl,
|
config: Config,
|
||||||
network: Network,
|
|
||||||
/// LWK Wollet, a watch-only Liquid wallet for this instance
|
/// LWK Wollet, a watch-only Liquid wallet for this instance
|
||||||
lwk_wollet: Arc<Mutex<LwkWollet>>,
|
lwk_wollet: Arc<Mutex<LwkWollet>>,
|
||||||
/// LWK Signer, for signing Liquid transactions
|
/// LWK Signer, for signing Liquid transactions
|
||||||
lwk_signer: SwSigner,
|
lwk_signer: SwSigner,
|
||||||
persister: Arc<Persister>,
|
persister: Arc<Persister>,
|
||||||
data_dir_path: String,
|
|
||||||
event_manager: Arc<EventManager>,
|
event_manager: Arc<EventManager>,
|
||||||
status_stream: Arc<BoltzStatusStream>,
|
status_stream: Arc<BoltzStatusStream>,
|
||||||
is_started: RwLock<bool>,
|
is_started: RwLock<bool>,
|
||||||
@@ -73,16 +69,15 @@ pub struct LiquidSdk {
|
|||||||
|
|
||||||
impl LiquidSdk {
|
impl LiquidSdk {
|
||||||
pub async fn connect(req: ConnectRequest) -> Result<Arc<LiquidSdk>> {
|
pub async fn connect(req: ConnectRequest) -> Result<Arc<LiquidSdk>> {
|
||||||
let is_mainnet = req.network == Network::Mainnet;
|
let config = req.config;
|
||||||
|
let is_mainnet = config.network == Network::Mainnet;
|
||||||
let signer = SwSigner::new(&req.mnemonic, is_mainnet)?;
|
let signer = SwSigner::new(&req.mnemonic, is_mainnet)?;
|
||||||
let descriptor = LiquidSdk::get_descriptor(&signer, req.network)?;
|
let descriptor = LiquidSdk::get_descriptor(&signer, config.network)?;
|
||||||
|
|
||||||
let sdk = LiquidSdk::new(LiquidSdkOptions {
|
let sdk = LiquidSdk::new(LiquidSdkOptions {
|
||||||
signer,
|
signer,
|
||||||
descriptor,
|
descriptor,
|
||||||
electrum_url: None,
|
config,
|
||||||
data_dir_path: req.data_dir,
|
|
||||||
network: req.network,
|
|
||||||
})?;
|
})?;
|
||||||
sdk.start().await?;
|
sdk.start().await?;
|
||||||
|
|
||||||
@@ -90,33 +85,30 @@ impl LiquidSdk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn new(opts: LiquidSdkOptions) -> Result<Arc<Self>> {
|
fn new(opts: LiquidSdkOptions) -> Result<Arc<Self>> {
|
||||||
let network = opts.network;
|
let config = opts.config;
|
||||||
let elements_network: ElementsNetwork = opts.network.into();
|
let elements_network: ElementsNetwork = config.network.into();
|
||||||
let electrum_url = opts.get_electrum_url();
|
|
||||||
let data_dir_path = opts.data_dir_path.unwrap_or(DEFAULT_DATA_DIR.to_string());
|
|
||||||
|
|
||||||
let lwk_persister = FsPersister::new(&data_dir_path, network.into(), &opts.descriptor)?;
|
fs::create_dir_all(&config.working_dir)?;
|
||||||
|
|
||||||
|
let lwk_persister = FsPersister::new(
|
||||||
|
config.working_dir.clone(),
|
||||||
|
elements_network,
|
||||||
|
&opts.descriptor,
|
||||||
|
)?;
|
||||||
let lwk_wollet = LwkWollet::new(elements_network, lwk_persister, opts.descriptor)?;
|
let lwk_wollet = LwkWollet::new(elements_network, lwk_persister, opts.descriptor)?;
|
||||||
|
|
||||||
fs::create_dir_all(&data_dir_path)?;
|
let persister = Arc::new(Persister::new(&config.working_dir, config.network)?);
|
||||||
|
|
||||||
let persister = Arc::new(Persister::new(&data_dir_path, network)?);
|
|
||||||
persister.init()?;
|
persister.init()?;
|
||||||
|
|
||||||
let event_manager = Arc::new(EventManager::new());
|
let event_manager = Arc::new(EventManager::new());
|
||||||
let status_stream = Arc::new(BoltzStatusStream::new(
|
let status_stream = Arc::new(BoltzStatusStream::new(&config.boltz_url, persister.clone()));
|
||||||
Self::boltz_url_v2(network),
|
|
||||||
persister.clone(),
|
|
||||||
));
|
|
||||||
let (shutdown_sender, shutdown_receiver) = watch::channel::<()>(());
|
let (shutdown_sender, shutdown_receiver) = watch::channel::<()>(());
|
||||||
|
|
||||||
let sdk = Arc::new(LiquidSdk {
|
let sdk = Arc::new(LiquidSdk {
|
||||||
|
config,
|
||||||
lwk_wollet: Arc::new(Mutex::new(lwk_wollet)),
|
lwk_wollet: Arc::new(Mutex::new(lwk_wollet)),
|
||||||
network,
|
|
||||||
electrum_url,
|
|
||||||
lwk_signer: opts.signer,
|
lwk_signer: opts.signer,
|
||||||
persister,
|
persister,
|
||||||
data_dir_path,
|
|
||||||
event_manager,
|
event_manager,
|
||||||
status_stream,
|
status_stream,
|
||||||
is_started: RwLock::new(false),
|
is_started: RwLock::new(false),
|
||||||
@@ -729,24 +721,7 @@ impl LiquidSdk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn boltz_client_v2(&self) -> BoltzApiClientV2 {
|
pub(crate) fn boltz_client_v2(&self) -> BoltzApiClientV2 {
|
||||||
BoltzApiClientV2::new(Self::boltz_url_v2(self.network))
|
BoltzApiClientV2::new(&self.config.boltz_url)
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn boltz_url_v2(network: Network) -> &'static str {
|
|
||||||
match network {
|
|
||||||
Network::Testnet => BOLTZ_TESTNET_URL_V2,
|
|
||||||
Network::Mainnet => BOLTZ_MAINNET_URL_V2,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn network_config(&self) -> ElectrumConfig {
|
|
||||||
ElectrumConfig::new(
|
|
||||||
self.network.into(),
|
|
||||||
&self.electrum_url.to_string(),
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
100,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn build_tx(
|
async fn build_tx(
|
||||||
@@ -756,7 +731,7 @@ impl LiquidSdk {
|
|||||||
amount_sat: u64,
|
amount_sat: u64,
|
||||||
) -> Result<Transaction, PaymentError> {
|
) -> Result<Transaction, PaymentError> {
|
||||||
let lwk_wollet = self.lwk_wollet.lock().await;
|
let lwk_wollet = self.lwk_wollet.lock().await;
|
||||||
let mut pset = lwk_wollet::TxBuilder::new(self.network.into())
|
let mut pset = lwk_wollet::TxBuilder::new(self.config.network.into())
|
||||||
.add_lbtc_recipient(
|
.add_lbtc_recipient(
|
||||||
&ElementsAddress::from_str(recipient_address).map_err(|e| {
|
&ElementsAddress::from_str(recipient_address).map_err(|e| {
|
||||||
PaymentError::Generic {
|
PaymentError::Generic {
|
||||||
@@ -780,7 +755,7 @@ impl LiquidSdk {
|
|||||||
.parse::<Bolt11Invoice>()
|
.parse::<Bolt11Invoice>()
|
||||||
.map_err(|_| PaymentError::InvalidInvoice)?;
|
.map_err(|_| PaymentError::InvalidInvoice)?;
|
||||||
|
|
||||||
match (invoice.network().to_string().as_str(), self.network) {
|
match (invoice.network().to_string().as_str(), self.config.network) {
|
||||||
("bitcoin", Network::Mainnet) => {}
|
("bitcoin", Network::Mainnet) => {}
|
||||||
("testnet", Network::Testnet) => {}
|
("testnet", Network::Testnet) => {}
|
||||||
_ => return Err(PaymentError::InvalidInvoice),
|
_ => return Err(PaymentError::InvalidInvoice),
|
||||||
@@ -815,7 +790,7 @@ impl LiquidSdk {
|
|||||||
async fn get_broadcast_fee_estimation(&self, amount_sat: u64) -> Result<u64> {
|
async fn get_broadcast_fee_estimation(&self, amount_sat: u64) -> Result<u64> {
|
||||||
// TODO Replace this with own address when LWK supports taproot
|
// TODO Replace this with own address when LWK supports taproot
|
||||||
// https://github.com/Blockstream/lwk/issues/31
|
// https://github.com/Blockstream/lwk/issues/31
|
||||||
let temp_p2tr_addr = match self.network {
|
let temp_p2tr_addr = match self.config.network {
|
||||||
Network::Mainnet => "lq1pqvzxvqhrf54dd4sny4cag7497pe38252qefk46t92frs7us8r80ja9ha8r5me09nn22m4tmdqp5p4wafq3s59cql3v9n45t5trwtxrmxfsyxjnstkctj",
|
Network::Mainnet => "lq1pqvzxvqhrf54dd4sny4cag7497pe38252qefk46t92frs7us8r80ja9ha8r5me09nn22m4tmdqp5p4wafq3s59cql3v9n45t5trwtxrmxfsyxjnstkctj",
|
||||||
Network::Testnet => "tlq1pq0wqu32e2xacxeyps22x8gjre4qk3u6r70pj4r62hzczxeyz8x3yxucrpn79zy28plc4x37aaf33kwt6dz2nn6gtkya6h02mwpzy4eh69zzexq7cf5y5"
|
Network::Testnet => "tlq1pq0wqu32e2xacxeyps22x8gjre4qk3u6r70pj4r62hzczxeyz8x3yxucrpn79zy28plc4x37aaf33kwt6dz2nn6gtkya6h02mwpzy4eh69zzexq7cf5y5"
|
||||||
};
|
};
|
||||||
@@ -871,12 +846,12 @@ impl LiquidSdk {
|
|||||||
swap_script: &LBtcSwapScriptV2,
|
swap_script: &LBtcSwapScriptV2,
|
||||||
) -> Result<LBtcSwapTxV2, PaymentError> {
|
) -> Result<LBtcSwapTxV2, PaymentError> {
|
||||||
let output_address = self.next_unused_address().await?.to_string();
|
let output_address = self.next_unused_address().await?.to_string();
|
||||||
let network_config = self.network_config();
|
let network_config = self.config.get_electrum_config();
|
||||||
Ok(LBtcSwapTxV2::new_refund(
|
Ok(LBtcSwapTxV2::new_refund(
|
||||||
swap_script.clone(),
|
swap_script.clone(),
|
||||||
&output_address,
|
&output_address,
|
||||||
&network_config,
|
&network_config,
|
||||||
Self::boltz_url_v2(self.network).to_string(),
|
self.config.clone().boltz_url,
|
||||||
swap_id.to_string(),
|
swap_id.to_string(),
|
||||||
)?)
|
)?)
|
||||||
}
|
}
|
||||||
@@ -895,7 +870,8 @@ impl LiquidSdk {
|
|||||||
Some((&self.boltz_client_v2(), &swap.id)),
|
Some((&self.boltz_client_v2(), &swap.id)),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let refund_tx_id = refund_tx.broadcast(&tx, &self.network_config(), is_lowball)?;
|
let refund_tx_id =
|
||||||
|
refund_tx.broadcast(&tx, &self.config.get_electrum_config(), is_lowball)?;
|
||||||
info!(
|
info!(
|
||||||
"Successfully broadcast cooperative refund for Send Swap {}",
|
"Successfully broadcast cooperative refund for Send Swap {}",
|
||||||
&swap.id
|
&swap.id
|
||||||
@@ -925,9 +901,9 @@ impl LiquidSdk {
|
|||||||
info!("locktime info: locktime_from_height = {locktime_from_height:?}, swap_script.locktime = {:?}", swap_script.locktime);
|
info!("locktime info: locktime_from_height = {locktime_from_height:?}, swap_script.locktime = {:?}", swap_script.locktime);
|
||||||
match utils::is_locktime_expired(locktime_from_height, swap_script.locktime) {
|
match utils::is_locktime_expired(locktime_from_height, swap_script.locktime) {
|
||||||
true => {
|
true => {
|
||||||
let tx =
|
let tx = refund_tx.sign_refund(&swap.get_refund_keypair()?, broadcast_fees_sat, None)?;
|
||||||
refund_tx.sign_refund(&swap.get_refund_keypair()?, broadcast_fees_sat, None)?;
|
let refund_tx_id =
|
||||||
let refund_tx_id = refund_tx.broadcast(&tx, &self.network_config(), is_lowball)?;
|
refund_tx.broadcast(&tx, &self.config.get_electrum_config(), is_lowball)?;
|
||||||
info!(
|
info!(
|
||||||
"Successfully broadcast non-cooperative refund for Send Swap {}",
|
"Successfully broadcast non-cooperative refund for Send Swap {}",
|
||||||
swap.id
|
swap.id
|
||||||
@@ -950,7 +926,7 @@ impl LiquidSdk {
|
|||||||
let broadcast_fees_sat =
|
let broadcast_fees_sat =
|
||||||
Amount::from_sat(self.get_broadcast_fee_estimation(amount_sat).await?);
|
Amount::from_sat(self.get_broadcast_fee_estimation(amount_sat).await?);
|
||||||
let client = self.boltz_client_v2();
|
let client = self.boltz_client_v2();
|
||||||
let is_lowball = match self.network {
|
let is_lowball = match self.config.network {
|
||||||
Network::Mainnet => None,
|
Network::Mainnet => None,
|
||||||
Network::Testnet => Some((&client, boltz_client::network::Chain::LiquidTestnet)),
|
Network::Testnet => Some((&client, boltz_client::network::Chain::LiquidTestnet)),
|
||||||
};
|
};
|
||||||
@@ -1031,7 +1007,7 @@ impl LiquidSdk {
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let electrum_client = ElectrumClient::new(&self.electrum_url)?;
|
let electrum_client = self.config.get_electrum_client()?;
|
||||||
let lockup_tx_id = electrum_client.broadcast(&lockup_tx)?.to_string();
|
let lockup_tx_id = electrum_client.broadcast(&lockup_tx)?.to_string();
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
@@ -1040,6 +1016,13 @@ impl LiquidSdk {
|
|||||||
Ok(lockup_tx_id)
|
Ok(lockup_tx_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates, initiates and starts monitoring the progress of a Send Payment.
|
||||||
|
///
|
||||||
|
/// Depending on [Config]'s `payment_timeout_sec`, this function will return:
|
||||||
|
/// - a [PaymentError::PaymentTimeout], if the payment could not be initiated in this time
|
||||||
|
/// - a [PaymentState::Pending] payment, if the payment could be initiated, but didn't yet
|
||||||
|
/// complete in this time
|
||||||
|
/// - a [PaymentState::Complete] payment, if the payment was successfully completed in this time
|
||||||
pub async fn send_payment(
|
pub async fn send_payment(
|
||||||
&self,
|
&self,
|
||||||
req: &PrepareSendResponse,
|
req: &PrepareSendResponse,
|
||||||
@@ -1110,7 +1093,7 @@ impl LiquidSdk {
|
|||||||
swap_id: String,
|
swap_id: String,
|
||||||
accept_zero_conf: bool,
|
accept_zero_conf: bool,
|
||||||
) -> Result<Payment, PaymentError> {
|
) -> Result<Payment, PaymentError> {
|
||||||
let timeout_fut = tokio::time::sleep(Duration::from_secs(15));
|
let timeout_fut = tokio::time::sleep(Duration::from_secs(self.config.payment_timeout_sec));
|
||||||
tokio::pin!(timeout_fut);
|
tokio::pin!(timeout_fut);
|
||||||
|
|
||||||
let mut events_stream = self.event_manager.subscribe();
|
let mut events_stream = self.event_manager.subscribe();
|
||||||
@@ -1178,8 +1161,8 @@ impl LiquidSdk {
|
|||||||
let claim_tx_wrapper = LBtcSwapTxV2::new_claim(
|
let claim_tx_wrapper = LBtcSwapTxV2::new_claim(
|
||||||
swap_script,
|
swap_script,
|
||||||
claim_address,
|
claim_address,
|
||||||
&self.network_config(),
|
&self.config.get_electrum_config(),
|
||||||
Self::boltz_url_v2(self.network).into(),
|
self.config.clone().boltz_url,
|
||||||
ongoing_receive_swap.id.clone(),
|
ongoing_receive_swap.id.clone(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -1194,8 +1177,8 @@ impl LiquidSdk {
|
|||||||
|
|
||||||
let claim_tx_id = claim_tx_wrapper.broadcast(
|
let claim_tx_id = claim_tx_wrapper.broadcast(
|
||||||
&claim_tx,
|
&claim_tx,
|
||||||
&self.network_config(),
|
&self.config.get_electrum_config(),
|
||||||
Some((&self.boltz_client_v2(), self.network.into())),
|
Some((&self.boltz_client_v2(), self.config.network.into())),
|
||||||
)?;
|
)?;
|
||||||
info!("Successfully broadcast claim tx {claim_tx_id} for Receive Swap {swap_id}");
|
info!("Successfully broadcast claim tx {claim_tx_id} for Receive Swap {swap_id}");
|
||||||
debug!("Claim Tx {:?}", claim_tx);
|
debug!("Claim Tx {:?}", claim_tx);
|
||||||
@@ -1329,7 +1312,7 @@ impl LiquidSdk {
|
|||||||
/// it inserts or updates a corresponding entry in our Payments table.
|
/// it inserts or updates a corresponding entry in our Payments table.
|
||||||
async fn sync_payments_with_chain_data(&self, with_scan: bool) -> Result<()> {
|
async fn sync_payments_with_chain_data(&self, with_scan: bool) -> Result<()> {
|
||||||
if with_scan {
|
if with_scan {
|
||||||
let mut electrum_client = ElectrumClient::new(&self.electrum_url)?;
|
let mut electrum_client = self.config.get_electrum_client()?;
|
||||||
let mut lwk_wollet = self.lwk_wollet.lock().await;
|
let mut lwk_wollet = self.lwk_wollet.lock().await;
|
||||||
lwk_wollet::full_scan_with_electrum_client(&mut lwk_wollet, &mut electrum_client)?;
|
lwk_wollet::full_scan_with_electrum_client(&mut lwk_wollet, &mut electrum_client)?;
|
||||||
}
|
}
|
||||||
@@ -1378,9 +1361,11 @@ impl LiquidSdk {
|
|||||||
info!("Retrieving preimage from non-cooperative claim tx");
|
info!("Retrieving preimage from non-cooperative claim tx");
|
||||||
|
|
||||||
let id = &swap.id;
|
let id = &swap.id;
|
||||||
let electrum_client = ElectrumClient::new(&self.electrum_url)?;
|
let electrum_client = self.config.get_electrum_client()?;
|
||||||
let swap_script = swap.get_swap_script()?;
|
let swap_script = swap.get_swap_script()?;
|
||||||
let swap_script_pk = swap_script.to_address(self.network.into())?.script_pubkey();
|
let swap_script_pk = swap_script
|
||||||
|
.to_address(self.config.network.into())?
|
||||||
|
.script_pubkey();
|
||||||
debug!("Found Send Swap swap_script_pk: {swap_script_pk:?}");
|
debug!("Found Send Swap swap_script_pk: {swap_script_pk:?}");
|
||||||
|
|
||||||
// Get tx history of the swap script (lockup address)
|
// Get tx history of the swap script (lockup address)
|
||||||
@@ -1445,8 +1430,8 @@ impl LiquidSdk {
|
|||||||
|
|
||||||
/// Empties all Liquid Wallet caches for this network type.
|
/// Empties all Liquid Wallet caches for this network type.
|
||||||
pub fn empty_wallet_cache(&self) -> Result<()> {
|
pub fn empty_wallet_cache(&self) -> Result<()> {
|
||||||
let mut path = PathBuf::from(self.data_dir_path.clone());
|
let mut path = PathBuf::from(self.config.working_dir.clone());
|
||||||
path.push(Into::<ElementsNetwork>::into(self.network).as_str());
|
path.push(Into::<ElementsNetwork>::into(self.config.network).as_str());
|
||||||
path.push("enc_cache");
|
path.push("enc_cache");
|
||||||
|
|
||||||
fs::remove_dir_all(&path)?;
|
fs::remove_dir_all(&path)?;
|
||||||
@@ -1484,6 +1469,13 @@ impl LiquidSdk {
|
|||||||
self.persister.restore_from_backup(backup_path)
|
self.persister.restore_from_backup(backup_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn default_config(network: Network) -> Config {
|
||||||
|
match network {
|
||||||
|
Network::Mainnet => Config::mainnet(),
|
||||||
|
Network::Testnet => Config::testnet(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_invoice(input: &str) -> Result<LNInvoice, PaymentError> {
|
pub fn parse_invoice(input: &str) -> Result<LNInvoice, PaymentError> {
|
||||||
let input = input
|
let input = input
|
||||||
.strip_prefix("lightning:")
|
.strip_prefix("lightning:")
|
||||||
@@ -1566,7 +1558,7 @@ mod tests {
|
|||||||
use tempdir::TempDir;
|
use tempdir::TempDir;
|
||||||
|
|
||||||
use crate::model::*;
|
use crate::model::*;
|
||||||
use crate::sdk::{LiquidSdk, Network};
|
use crate::sdk::LiquidSdk;
|
||||||
|
|
||||||
const TEST_MNEMONIC: &str = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
|
const TEST_MNEMONIC: &str = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
|
||||||
|
|
||||||
@@ -1594,10 +1586,11 @@ mod tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn normal_submarine_swap() -> Result<()> {
|
async fn normal_submarine_swap() -> Result<()> {
|
||||||
let (_data_dir, data_dir_str) = create_temp_dir()?;
|
let (_data_dir, data_dir_str) = create_temp_dir()?;
|
||||||
|
let mut config = Config::testnet();
|
||||||
|
config.working_dir = data_dir_str;
|
||||||
let sdk = LiquidSdk::connect(ConnectRequest {
|
let sdk = LiquidSdk::connect(ConnectRequest {
|
||||||
mnemonic: TEST_MNEMONIC.to_string(),
|
mnemonic: TEST_MNEMONIC.to_string(),
|
||||||
data_dir: Some(data_dir_str),
|
config,
|
||||||
network: Network::Testnet,
|
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@@ -1612,10 +1605,11 @@ mod tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn reverse_submarine_swap() -> Result<()> {
|
async fn reverse_submarine_swap() -> Result<()> {
|
||||||
let (_data_dir, data_dir_str) = create_temp_dir()?;
|
let (_data_dir, data_dir_str) = create_temp_dir()?;
|
||||||
|
let mut config = Config::testnet();
|
||||||
|
config.working_dir = data_dir_str;
|
||||||
let sdk = LiquidSdk::connect(ConnectRequest {
|
let sdk = LiquidSdk::connect(ConnectRequest {
|
||||||
mnemonic: TEST_MNEMONIC.to_string(),
|
mnemonic: TEST_MNEMONIC.to_string(),
|
||||||
data_dir: Some(data_dir_str),
|
config,
|
||||||
network: Network::Testnet,
|
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ Future<BindingLiquidSdk> connect({required ConnectRequest req, dynamic hint}) =>
|
|||||||
Stream<LogEntry> breezLogStream({dynamic hint}) =>
|
Stream<LogEntry> breezLogStream({dynamic hint}) =>
|
||||||
RustLib.instance.api.crateBindingsBreezLogStream(hint: hint);
|
RustLib.instance.api.crateBindingsBreezLogStream(hint: hint);
|
||||||
|
|
||||||
|
Future<Config> defaultConfig({required Network network, dynamic hint}) =>
|
||||||
|
RustLib.instance.api.crateBindingsDefaultConfig(network: network, hint: hint);
|
||||||
|
|
||||||
Future<LNInvoice> parseInvoice({required String input, dynamic hint}) =>
|
Future<LNInvoice> parseInvoice({required String input, dynamic hint}) =>
|
||||||
RustLib.instance.api.crateBindingsParseInvoice(input: input, hint: hint);
|
RustLib.instance.api.crateBindingsParseInvoice(input: input, hint: hint);
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ class RustLib extends BaseEntrypoint<RustLibApi, RustLibApiImpl, RustLibWire> {
|
|||||||
String get codegenVersion => '2.0.0-dev.36';
|
String get codegenVersion => '2.0.0-dev.36';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get rustContentHash => -532134055;
|
int get rustContentHash => 1028270774;
|
||||||
|
|
||||||
static const kDefaultExternalLibraryLoaderConfig = ExternalLibraryLoaderConfig(
|
static const kDefaultExternalLibraryLoaderConfig = ExternalLibraryLoaderConfig(
|
||||||
stem: 'breez_liquid_sdk',
|
stem: 'breez_liquid_sdk',
|
||||||
@@ -100,6 +100,8 @@ abstract class RustLibApi extends BaseApi {
|
|||||||
|
|
||||||
Future<BindingLiquidSdk> crateBindingsConnect({required ConnectRequest req, dynamic hint});
|
Future<BindingLiquidSdk> crateBindingsConnect({required ConnectRequest req, dynamic hint});
|
||||||
|
|
||||||
|
Future<Config> crateBindingsDefaultConfig({required Network network, dynamic hint});
|
||||||
|
|
||||||
Future<LNInvoice> crateBindingsParseInvoice({required String input, dynamic hint});
|
Future<LNInvoice> crateBindingsParseInvoice({required String input, dynamic hint});
|
||||||
|
|
||||||
RustArcIncrementStrongCountFnType get rust_arc_increment_strong_count_BindingLiquidSdk;
|
RustArcIncrementStrongCountFnType get rust_arc_increment_strong_count_BindingLiquidSdk;
|
||||||
@@ -485,6 +487,29 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
|||||||
argNames: ["req"],
|
argNames: ["req"],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Config> crateBindingsDefaultConfig({required Network network, dynamic hint}) {
|
||||||
|
return handler.executeNormal(NormalTask(
|
||||||
|
callFfi: (port_) {
|
||||||
|
var arg0 = cst_encode_network(network);
|
||||||
|
return wire.wire__crate__bindings__default_config(port_, arg0);
|
||||||
|
},
|
||||||
|
codec: DcoCodec(
|
||||||
|
decodeSuccessData: dco_decode_config,
|
||||||
|
decodeErrorData: null,
|
||||||
|
),
|
||||||
|
constMeta: kCrateBindingsDefaultConfigConstMeta,
|
||||||
|
argValues: [network],
|
||||||
|
apiImpl: this,
|
||||||
|
hint: hint,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskConstMeta get kCrateBindingsDefaultConfigConstMeta => const TaskConstMeta(
|
||||||
|
debugName: "default_config",
|
||||||
|
argNames: ["network"],
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<LNInvoice> crateBindingsParseInvoice({required String input, dynamic hint}) {
|
Future<LNInvoice> crateBindingsParseInvoice({required String input, dynamic hint}) {
|
||||||
return handler.executeNormal(NormalTask(
|
return handler.executeNormal(NormalTask(
|
||||||
@@ -637,15 +662,28 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
|||||||
return dco_decode_u_64(raw);
|
return dco_decode_u_64(raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
Config dco_decode_config(dynamic raw) {
|
||||||
|
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||||
|
final arr = raw as List<dynamic>;
|
||||||
|
if (arr.length != 5) throw Exception('unexpected arr length: expect 5 but see ${arr.length}');
|
||||||
|
return Config(
|
||||||
|
boltzUrl: dco_decode_String(arr[0]),
|
||||||
|
electrumUrl: dco_decode_String(arr[1]),
|
||||||
|
workingDir: dco_decode_String(arr[2]),
|
||||||
|
network: dco_decode_network(arr[3]),
|
||||||
|
paymentTimeoutSec: dco_decode_u_64(arr[4]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
ConnectRequest dco_decode_connect_request(dynamic raw) {
|
ConnectRequest dco_decode_connect_request(dynamic raw) {
|
||||||
// Codec=Dco (DartCObject based), see doc to use other codecs
|
// Codec=Dco (DartCObject based), see doc to use other codecs
|
||||||
final arr = raw as List<dynamic>;
|
final arr = raw as List<dynamic>;
|
||||||
if (arr.length != 3) throw Exception('unexpected arr length: expect 3 but see ${arr.length}');
|
if (arr.length != 2) throw Exception('unexpected arr length: expect 2 but see ${arr.length}');
|
||||||
return ConnectRequest(
|
return ConnectRequest(
|
||||||
mnemonic: dco_decode_String(arr[0]),
|
mnemonic: dco_decode_String(arr[0]),
|
||||||
dataDir: dco_decode_opt_String(arr[1]),
|
config: dco_decode_config(arr[1]),
|
||||||
network: dco_decode_network(arr[2]),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1134,13 +1172,28 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
|||||||
return (sse_decode_u_64(deserializer));
|
return (sse_decode_u_64(deserializer));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
Config sse_decode_config(SseDeserializer deserializer) {
|
||||||
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
|
var var_boltzUrl = sse_decode_String(deserializer);
|
||||||
|
var var_electrumUrl = sse_decode_String(deserializer);
|
||||||
|
var var_workingDir = sse_decode_String(deserializer);
|
||||||
|
var var_network = sse_decode_network(deserializer);
|
||||||
|
var var_paymentTimeoutSec = sse_decode_u_64(deserializer);
|
||||||
|
return Config(
|
||||||
|
boltzUrl: var_boltzUrl,
|
||||||
|
electrumUrl: var_electrumUrl,
|
||||||
|
workingDir: var_workingDir,
|
||||||
|
network: var_network,
|
||||||
|
paymentTimeoutSec: var_paymentTimeoutSec);
|
||||||
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
ConnectRequest sse_decode_connect_request(SseDeserializer deserializer) {
|
ConnectRequest sse_decode_connect_request(SseDeserializer deserializer) {
|
||||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
var var_mnemonic = sse_decode_String(deserializer);
|
var var_mnemonic = sse_decode_String(deserializer);
|
||||||
var var_dataDir = sse_decode_opt_String(deserializer);
|
var var_config = sse_decode_config(deserializer);
|
||||||
var var_network = sse_decode_network(deserializer);
|
return ConnectRequest(mnemonic: var_mnemonic, config: var_config);
|
||||||
return ConnectRequest(mnemonic: var_mnemonic, dataDir: var_dataDir, network: var_network);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
@@ -1719,12 +1772,21 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
|||||||
sse_encode_u_64(self, serializer);
|
sse_encode_u_64(self, serializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_config(Config self, SseSerializer serializer) {
|
||||||
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
|
sse_encode_String(self.boltzUrl, serializer);
|
||||||
|
sse_encode_String(self.electrumUrl, serializer);
|
||||||
|
sse_encode_String(self.workingDir, serializer);
|
||||||
|
sse_encode_network(self.network, serializer);
|
||||||
|
sse_encode_u_64(self.paymentTimeoutSec, serializer);
|
||||||
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
void sse_encode_connect_request(ConnectRequest self, SseSerializer serializer) {
|
void sse_encode_connect_request(ConnectRequest self, SseSerializer serializer) {
|
||||||
// Codec=Sse (Serialization based), see doc to use other codecs
|
// Codec=Sse (Serialization based), see doc to use other codecs
|
||||||
sse_encode_String(self.mnemonic, serializer);
|
sse_encode_String(self.mnemonic, serializer);
|
||||||
sse_encode_opt_String(self.dataDir, serializer);
|
sse_encode_config(self.config, serializer);
|
||||||
sse_encode_network(self.network, serializer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
|
|||||||
@@ -85,6 +85,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||||||
@protected
|
@protected
|
||||||
BigInt dco_decode_box_autoadd_u_64(dynamic raw);
|
BigInt dco_decode_box_autoadd_u_64(dynamic raw);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
Config dco_decode_config(dynamic raw);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
ConnectRequest dco_decode_connect_request(dynamic raw);
|
ConnectRequest dco_decode_connect_request(dynamic raw);
|
||||||
|
|
||||||
@@ -246,6 +249,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||||||
@protected
|
@protected
|
||||||
BigInt sse_decode_box_autoadd_u_64(SseDeserializer deserializer);
|
BigInt sse_decode_box_autoadd_u_64(SseDeserializer deserializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
Config sse_decode_config(SseDeserializer deserializer);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
ConnectRequest sse_decode_connect_request(SseDeserializer deserializer);
|
ConnectRequest sse_decode_connect_request(SseDeserializer deserializer);
|
||||||
|
|
||||||
@@ -575,11 +581,19 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||||||
cst_api_fill_to_wire_restore_request(apiObj, wireObj.ref);
|
cst_api_fill_to_wire_restore_request(apiObj, wireObj.ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void cst_api_fill_to_wire_config(Config apiObj, wire_cst_config wireObj) {
|
||||||
|
wireObj.boltz_url = cst_encode_String(apiObj.boltzUrl);
|
||||||
|
wireObj.electrum_url = cst_encode_String(apiObj.electrumUrl);
|
||||||
|
wireObj.working_dir = cst_encode_String(apiObj.workingDir);
|
||||||
|
wireObj.network = cst_encode_network(apiObj.network);
|
||||||
|
wireObj.payment_timeout_sec = cst_encode_u_64(apiObj.paymentTimeoutSec);
|
||||||
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
void cst_api_fill_to_wire_connect_request(ConnectRequest apiObj, wire_cst_connect_request wireObj) {
|
void cst_api_fill_to_wire_connect_request(ConnectRequest apiObj, wire_cst_connect_request wireObj) {
|
||||||
wireObj.mnemonic = cst_encode_String(apiObj.mnemonic);
|
wireObj.mnemonic = cst_encode_String(apiObj.mnemonic);
|
||||||
wireObj.data_dir = cst_encode_opt_String(apiObj.dataDir);
|
cst_api_fill_to_wire_config(apiObj.config, wireObj.config);
|
||||||
wireObj.network = cst_encode_network(apiObj.network);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
@@ -922,6 +936,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
|||||||
@protected
|
@protected
|
||||||
void sse_encode_box_autoadd_u_64(BigInt self, SseSerializer serializer);
|
void sse_encode_box_autoadd_u_64(BigInt self, SseSerializer serializer);
|
||||||
|
|
||||||
|
@protected
|
||||||
|
void sse_encode_config(Config self, SseSerializer serializer);
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
void sse_encode_connect_request(ConnectRequest self, SseSerializer serializer);
|
void sse_encode_connect_request(ConnectRequest self, SseSerializer serializer);
|
||||||
|
|
||||||
@@ -1308,6 +1325,22 @@ class RustLibWire implements BaseWire {
|
|||||||
late final _wire__crate__bindings__connect = _wire__crate__bindings__connectPtr
|
late final _wire__crate__bindings__connect = _wire__crate__bindings__connectPtr
|
||||||
.asFunction<void Function(int, ffi.Pointer<wire_cst_connect_request>)>();
|
.asFunction<void Function(int, ffi.Pointer<wire_cst_connect_request>)>();
|
||||||
|
|
||||||
|
void wire__crate__bindings__default_config(
|
||||||
|
int port_,
|
||||||
|
int network,
|
||||||
|
) {
|
||||||
|
return _wire__crate__bindings__default_config(
|
||||||
|
port_,
|
||||||
|
network,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
late final _wire__crate__bindings__default_configPtr =
|
||||||
|
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64, ffi.Int32)>>(
|
||||||
|
'frbgen_breez_liquid_wire__crate__bindings__default_config');
|
||||||
|
late final _wire__crate__bindings__default_config =
|
||||||
|
_wire__crate__bindings__default_configPtr.asFunction<void Function(int, int)>();
|
||||||
|
|
||||||
void wire__crate__bindings__parse_invoice(
|
void wire__crate__bindings__parse_invoice(
|
||||||
int port_,
|
int port_,
|
||||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> input,
|
ffi.Pointer<wire_cst_list_prim_u_8_strict> input,
|
||||||
@@ -1577,13 +1610,24 @@ final class wire_cst_prepare_send_response extends ffi.Struct {
|
|||||||
external int fees_sat;
|
external int fees_sat;
|
||||||
}
|
}
|
||||||
|
|
||||||
final class wire_cst_connect_request extends ffi.Struct {
|
final class wire_cst_config extends ffi.Struct {
|
||||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> mnemonic;
|
external ffi.Pointer<wire_cst_list_prim_u_8_strict> boltz_url;
|
||||||
|
|
||||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> data_dir;
|
external ffi.Pointer<wire_cst_list_prim_u_8_strict> electrum_url;
|
||||||
|
|
||||||
|
external ffi.Pointer<wire_cst_list_prim_u_8_strict> working_dir;
|
||||||
|
|
||||||
@ffi.Int32()
|
@ffi.Int32()
|
||||||
external int network;
|
external int network;
|
||||||
|
|
||||||
|
@ffi.Uint64()
|
||||||
|
external int payment_timeout_sec;
|
||||||
|
}
|
||||||
|
|
||||||
|
final class wire_cst_connect_request extends ffi.Struct {
|
||||||
|
external ffi.Pointer<wire_cst_list_prim_u_8_strict> mnemonic;
|
||||||
|
|
||||||
|
external wire_cst_config config;
|
||||||
}
|
}
|
||||||
|
|
||||||
final class wire_cst_payment extends ffi.Struct {
|
final class wire_cst_payment extends ffi.Struct {
|
||||||
|
|||||||
@@ -28,19 +28,55 @@ class BackupRequest {
|
|||||||
other is BackupRequest && runtimeType == other.runtimeType && backupPath == other.backupPath;
|
other is BackupRequest && runtimeType == other.runtimeType && backupPath == other.backupPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ConnectRequest {
|
/// Configuration for the Liquid SDK
|
||||||
final String mnemonic;
|
class Config {
|
||||||
final String? dataDir;
|
final String boltzUrl;
|
||||||
final Network network;
|
final String electrumUrl;
|
||||||
|
|
||||||
const ConnectRequest({
|
/// Directory in which all SDK files (DB, log) are stored.
|
||||||
required this.mnemonic,
|
final String workingDir;
|
||||||
this.dataDir,
|
final Network network;
|
||||||
|
final BigInt paymentTimeoutSec;
|
||||||
|
|
||||||
|
const Config({
|
||||||
|
required this.boltzUrl,
|
||||||
|
required this.electrumUrl,
|
||||||
|
required this.workingDir,
|
||||||
required this.network,
|
required this.network,
|
||||||
|
required this.paymentTimeoutSec,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => mnemonic.hashCode ^ dataDir.hashCode ^ network.hashCode;
|
int get hashCode =>
|
||||||
|
boltzUrl.hashCode ^
|
||||||
|
electrumUrl.hashCode ^
|
||||||
|
workingDir.hashCode ^
|
||||||
|
network.hashCode ^
|
||||||
|
paymentTimeoutSec.hashCode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
other is Config &&
|
||||||
|
runtimeType == other.runtimeType &&
|
||||||
|
boltzUrl == other.boltzUrl &&
|
||||||
|
electrumUrl == other.electrumUrl &&
|
||||||
|
workingDir == other.workingDir &&
|
||||||
|
network == other.network &&
|
||||||
|
paymentTimeoutSec == other.paymentTimeoutSec;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConnectRequest {
|
||||||
|
final String mnemonic;
|
||||||
|
final Config config;
|
||||||
|
|
||||||
|
const ConnectRequest({
|
||||||
|
required this.mnemonic,
|
||||||
|
required this.config,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => mnemonic.hashCode ^ config.hashCode;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
@@ -48,8 +84,7 @@ class ConnectRequest {
|
|||||||
other is ConnectRequest &&
|
other is ConnectRequest &&
|
||||||
runtimeType == other.runtimeType &&
|
runtimeType == other.runtimeType &&
|
||||||
mnemonic == other.mnemonic &&
|
mnemonic == other.mnemonic &&
|
||||||
dataDir == other.dataDir &&
|
config == other.config;
|
||||||
network == other.network;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class GetInfoRequest {
|
class GetInfoRequest {
|
||||||
|
|||||||
@@ -294,6 +294,22 @@ class FlutterBreezLiquidBindings {
|
|||||||
_frbgen_breez_liquid_wire__crate__bindings__connectPtr
|
_frbgen_breez_liquid_wire__crate__bindings__connectPtr
|
||||||
.asFunction<void Function(int, ffi.Pointer<wire_cst_connect_request>)>();
|
.asFunction<void Function(int, ffi.Pointer<wire_cst_connect_request>)>();
|
||||||
|
|
||||||
|
void frbgen_breez_liquid_wire__crate__bindings__default_config(
|
||||||
|
int port_,
|
||||||
|
int network,
|
||||||
|
) {
|
||||||
|
return _frbgen_breez_liquid_wire__crate__bindings__default_config(
|
||||||
|
port_,
|
||||||
|
network,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
late final _frbgen_breez_liquid_wire__crate__bindings__default_configPtr =
|
||||||
|
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64, ffi.Int32)>>(
|
||||||
|
'frbgen_breez_liquid_wire__crate__bindings__default_config');
|
||||||
|
late final _frbgen_breez_liquid_wire__crate__bindings__default_config =
|
||||||
|
_frbgen_breez_liquid_wire__crate__bindings__default_configPtr.asFunction<void Function(int, int)>();
|
||||||
|
|
||||||
void frbgen_breez_liquid_wire__crate__bindings__parse_invoice(
|
void frbgen_breez_liquid_wire__crate__bindings__parse_invoice(
|
||||||
int port_,
|
int port_,
|
||||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> input,
|
ffi.Pointer<wire_cst_list_prim_u_8_strict> input,
|
||||||
@@ -589,13 +605,24 @@ final class wire_cst_prepare_send_response extends ffi.Struct {
|
|||||||
external int fees_sat;
|
external int fees_sat;
|
||||||
}
|
}
|
||||||
|
|
||||||
final class wire_cst_connect_request extends ffi.Struct {
|
final class wire_cst_config extends ffi.Struct {
|
||||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> mnemonic;
|
external ffi.Pointer<wire_cst_list_prim_u_8_strict> boltz_url;
|
||||||
|
|
||||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> data_dir;
|
external ffi.Pointer<wire_cst_list_prim_u_8_strict> electrum_url;
|
||||||
|
|
||||||
|
external ffi.Pointer<wire_cst_list_prim_u_8_strict> working_dir;
|
||||||
|
|
||||||
@ffi.Int32()
|
@ffi.Int32()
|
||||||
external int network;
|
external int network;
|
||||||
|
|
||||||
|
@ffi.Uint64()
|
||||||
|
external int payment_timeout_sec;
|
||||||
|
}
|
||||||
|
|
||||||
|
final class wire_cst_connect_request extends ffi.Struct {
|
||||||
|
external ffi.Pointer<wire_cst_list_prim_u_8_strict> mnemonic;
|
||||||
|
|
||||||
|
external wire_cst_config config;
|
||||||
}
|
}
|
||||||
|
|
||||||
final class wire_cst_payment extends ffi.Struct {
|
final class wire_cst_payment extends ffi.Struct {
|
||||||
|
|||||||
@@ -34,32 +34,78 @@ fun asBackupRequestList(arr: ReadableArray): List<BackupRequest> {
|
|||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
fun asConnectRequest(connectRequest: ReadableMap): ConnectRequest? {
|
fun asConfig(config: ReadableMap): Config? {
|
||||||
if (!validateMandatoryFields(
|
if (!validateMandatoryFields(
|
||||||
connectRequest,
|
config,
|
||||||
arrayOf(
|
arrayOf(
|
||||||
"mnemonic",
|
"boltzUrl",
|
||||||
|
"electrumUrl",
|
||||||
|
"workingDir",
|
||||||
"network",
|
"network",
|
||||||
|
"paymentTimeoutSec",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
val mnemonic = connectRequest.getString("mnemonic")!!
|
val boltzUrl = config.getString("boltzUrl")!!
|
||||||
val network = connectRequest.getString("network")?.let { asNetwork(it) }!!
|
val electrumUrl = config.getString("electrumUrl")!!
|
||||||
val dataDir = if (hasNonNullKey(connectRequest, "dataDir")) connectRequest.getString("dataDir") else null
|
val workingDir = config.getString("workingDir")!!
|
||||||
return ConnectRequest(
|
val network = config.getString("network")?.let { asNetwork(it) }!!
|
||||||
mnemonic,
|
val paymentTimeoutSec = config.getDouble("paymentTimeoutSec").toULong()
|
||||||
|
return Config(
|
||||||
|
boltzUrl,
|
||||||
|
electrumUrl,
|
||||||
|
workingDir,
|
||||||
network,
|
network,
|
||||||
dataDir,
|
paymentTimeoutSec,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readableMapOf(config: Config): ReadableMap {
|
||||||
|
return readableMapOf(
|
||||||
|
"boltzUrl" to config.boltzUrl,
|
||||||
|
"electrumUrl" to config.electrumUrl,
|
||||||
|
"workingDir" to config.workingDir,
|
||||||
|
"network" to config.network.name.lowercase(),
|
||||||
|
"paymentTimeoutSec" to config.paymentTimeoutSec,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun asConfigList(arr: ReadableArray): List<Config> {
|
||||||
|
val list = ArrayList<Config>()
|
||||||
|
for (value in arr.toArrayList()) {
|
||||||
|
when (value) {
|
||||||
|
is ReadableMap -> list.add(asConfig(value)!!)
|
||||||
|
else -> throw LiquidSdkException.Generic(errUnexpectedType("${value::class.java.name}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
fun asConnectRequest(connectRequest: ReadableMap): ConnectRequest? {
|
||||||
|
if (!validateMandatoryFields(
|
||||||
|
connectRequest,
|
||||||
|
arrayOf(
|
||||||
|
"config",
|
||||||
|
"mnemonic",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
val config = connectRequest.getMap("config")?.let { asConfig(it) }!!
|
||||||
|
val mnemonic = connectRequest.getString("mnemonic")!!
|
||||||
|
return ConnectRequest(
|
||||||
|
config,
|
||||||
|
mnemonic,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun readableMapOf(connectRequest: ConnectRequest): ReadableMap {
|
fun readableMapOf(connectRequest: ConnectRequest): ReadableMap {
|
||||||
return readableMapOf(
|
return readableMapOf(
|
||||||
|
"config" to readableMapOf(connectRequest.config),
|
||||||
"mnemonic" to connectRequest.mnemonic,
|
"mnemonic" to connectRequest.mnemonic,
|
||||||
"network" to connectRequest.network.name.lowercase(),
|
|
||||||
"dataDir" to connectRequest.dataDir,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.breezliquidsdk
|
|||||||
import breez_liquid_sdk.*
|
import breez_liquid_sdk.*
|
||||||
import com.facebook.react.bridge.*
|
import com.facebook.react.bridge.*
|
||||||
import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter
|
import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter
|
||||||
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ExecutorService
|
import java.util.concurrent.ExecutorService
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
@@ -34,12 +35,44 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext
|
|||||||
throw LiquidSdkException.Generic("Not initialized")
|
throw LiquidSdkException.Generic("Not initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throws(LiquidSdkException::class)
|
||||||
|
private fun ensureWorkingDir(workingDir: String) {
|
||||||
|
try {
|
||||||
|
val workingDirFile = File(workingDir)
|
||||||
|
|
||||||
|
if (!workingDirFile.exists() && !workingDirFile.mkdirs()) {
|
||||||
|
throw LiquidSdkException.Generic("Mandatory field workingDir must contain a writable directory")
|
||||||
|
}
|
||||||
|
} catch (e: SecurityException) {
|
||||||
|
throw LiquidSdkException.Generic("Mandatory field workingDir must contain a writable directory")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
fun addListener(eventName: String) {}
|
fun addListener(eventName: String) {}
|
||||||
|
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
fun removeListeners(count: Int) {}
|
fun removeListeners(count: Int) {}
|
||||||
|
|
||||||
|
@ReactMethod
|
||||||
|
fun defaultConfig(
|
||||||
|
network: String,
|
||||||
|
promise: Promise,
|
||||||
|
) {
|
||||||
|
executor.execute {
|
||||||
|
try {
|
||||||
|
val networkTmp = asNetwork(network)
|
||||||
|
val res = defaultConfig(networkTmp)
|
||||||
|
val workingDir = File(reactApplicationContext.filesDir.toString() + "/breezLiquidSdk")
|
||||||
|
|
||||||
|
res.workingDir = workingDir.absolutePath
|
||||||
|
promise.resolve(readableMapOf(res))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
fun parseInvoice(
|
fun parseInvoice(
|
||||||
invoice: String,
|
invoice: String,
|
||||||
@@ -86,9 +119,9 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext
|
|||||||
asConnectRequest(
|
asConnectRequest(
|
||||||
req,
|
req,
|
||||||
) ?: run { throw LiquidSdkException.Generic(errMissingMandatoryField("req", "ConnectRequest")) }
|
) ?: run { throw LiquidSdkException.Generic(errMissingMandatoryField("req", "ConnectRequest")) }
|
||||||
connectRequest.dataDir = connectRequest.dataDir?.takeUnless {
|
|
||||||
it.isEmpty()
|
ensureWorkingDir(connectRequest.config.workingDir)
|
||||||
} ?: run { reactApplicationContext.filesDir.toString() + "/breezLiquidSdk" }
|
|
||||||
bindingLiquidSdk = connect(connectRequest)
|
bindingLiquidSdk = connect(connectRequest)
|
||||||
promise.resolve(readableMapOf("status" to "ok"))
|
promise.resolve(readableMapOf("status" to "ok"))
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|||||||
7
packages/react-native/example/App.js
vendored
7
packages/react-native/example/App.js
vendored
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import React, { useState } from "react"
|
import React, { useState } from "react"
|
||||||
import { SafeAreaView, ScrollView, StatusBar, Text, TouchableOpacity, View } from "react-native"
|
import { SafeAreaView, ScrollView, StatusBar, Text, TouchableOpacity, View } from "react-native"
|
||||||
import { addEventListener, Network, getInfo, connect, removeEventListener } from "@breeztech/react-native-breez-liquid-sdk"
|
import { addEventListener, connect, defaultConfig, getInfo, Network, removeEventListener } from "@breeztech/react-native-breez-liquid-sdk"
|
||||||
import { generateMnemonic } from "@dreson4/react-native-quick-bip39"
|
import { generateMnemonic } from "@dreson4/react-native-quick-bip39"
|
||||||
import { getSecureItem, setSecureItem } from "./utils/storage"
|
import { getSecureItem, setSecureItem } from "./utils/storage"
|
||||||
|
|
||||||
@@ -49,7 +49,10 @@ const App = () => {
|
|||||||
setSecureItem(MNEMONIC_STORE, mnemonic)
|
setSecureItem(MNEMONIC_STORE, mnemonic)
|
||||||
}
|
}
|
||||||
|
|
||||||
await connect({ mnemonic, network: Network.LIQUID_TESTNET })
|
const config = await defaultConfig(Network.TESTNET)
|
||||||
|
addLine("defaultConfig", JSON.stringify(config))
|
||||||
|
|
||||||
|
await connect({ config, mnemonic })
|
||||||
addLine("connect", null)
|
addLine("connect", null)
|
||||||
|
|
||||||
listenerId = await addEventListener(eventHandler)
|
listenerId = await addEventListener(eventHandler)
|
||||||
|
|||||||
@@ -38,35 +38,81 @@ enum BreezLiquidSDKMapper {
|
|||||||
return backupRequestList.map { v -> [String: Any?] in dictionaryOf(backupRequest: v) }
|
return backupRequestList.map { v -> [String: Any?] in dictionaryOf(backupRequest: v) }
|
||||||
}
|
}
|
||||||
|
|
||||||
static func asConnectRequest(connectRequest: [String: Any?]) throws -> ConnectRequest {
|
static func asConfig(config: [String: Any?]) throws -> Config {
|
||||||
guard let mnemonic = connectRequest["mnemonic"] as? String else {
|
guard let boltzUrl = config["boltzUrl"] as? String else {
|
||||||
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "mnemonic", typeName: "ConnectRequest"))
|
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "boltzUrl", typeName: "Config"))
|
||||||
}
|
}
|
||||||
guard let networkTmp = connectRequest["network"] as? String else {
|
guard let electrumUrl = config["electrumUrl"] as? String else {
|
||||||
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "network", typeName: "ConnectRequest"))
|
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "electrumUrl", typeName: "Config"))
|
||||||
|
}
|
||||||
|
guard let workingDir = config["workingDir"] as? String else {
|
||||||
|
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "workingDir", typeName: "Config"))
|
||||||
|
}
|
||||||
|
guard let networkTmp = config["network"] as? String else {
|
||||||
|
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "network", typeName: "Config"))
|
||||||
}
|
}
|
||||||
let network = try asNetwork(network: networkTmp)
|
let network = try asNetwork(network: networkTmp)
|
||||||
|
|
||||||
var dataDir: String?
|
guard let paymentTimeoutSec = config["paymentTimeoutSec"] as? UInt64 else {
|
||||||
if hasNonNilKey(data: connectRequest, key: "dataDir") {
|
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "paymentTimeoutSec", typeName: "Config"))
|
||||||
guard let dataDirTmp = connectRequest["dataDir"] as? String else {
|
}
|
||||||
throw LiquidSdkError.Generic(message: errUnexpectedValue(fieldName: "dataDir"))
|
|
||||||
|
return Config(
|
||||||
|
boltzUrl: boltzUrl,
|
||||||
|
electrumUrl: electrumUrl,
|
||||||
|
workingDir: workingDir,
|
||||||
|
network: network,
|
||||||
|
paymentTimeoutSec: paymentTimeoutSec
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func dictionaryOf(config: Config) -> [String: Any?] {
|
||||||
|
return [
|
||||||
|
"boltzUrl": config.boltzUrl,
|
||||||
|
"electrumUrl": config.electrumUrl,
|
||||||
|
"workingDir": config.workingDir,
|
||||||
|
"network": valueOf(network: config.network),
|
||||||
|
"paymentTimeoutSec": config.paymentTimeoutSec,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
static func asConfigList(arr: [Any]) throws -> [Config] {
|
||||||
|
var list = [Config]()
|
||||||
|
for value in arr {
|
||||||
|
if let val = value as? [String: Any?] {
|
||||||
|
var config = try asConfig(config: val)
|
||||||
|
list.append(config)
|
||||||
|
} else {
|
||||||
|
throw LiquidSdkError.Generic(message: errUnexpectedType(typeName: "Config"))
|
||||||
}
|
}
|
||||||
dataDir = dataDirTmp
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
static func arrayOf(configList: [Config]) -> [Any] {
|
||||||
|
return configList.map { v -> [String: Any?] in dictionaryOf(config: v) }
|
||||||
|
}
|
||||||
|
|
||||||
|
static func asConnectRequest(connectRequest: [String: Any?]) throws -> ConnectRequest {
|
||||||
|
guard let configTmp = connectRequest["config"] as? [String: Any?] else {
|
||||||
|
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "config", typeName: "ConnectRequest"))
|
||||||
|
}
|
||||||
|
let config = try asConfig(config: configTmp)
|
||||||
|
|
||||||
|
guard let mnemonic = connectRequest["mnemonic"] as? String else {
|
||||||
|
throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "mnemonic", typeName: "ConnectRequest"))
|
||||||
}
|
}
|
||||||
|
|
||||||
return ConnectRequest(
|
return ConnectRequest(
|
||||||
mnemonic: mnemonic,
|
config: config,
|
||||||
network: network,
|
mnemonic: mnemonic
|
||||||
dataDir: dataDir
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
static func dictionaryOf(connectRequest: ConnectRequest) -> [String: Any?] {
|
static func dictionaryOf(connectRequest: ConnectRequest) -> [String: Any?] {
|
||||||
return [
|
return [
|
||||||
|
"config": dictionaryOf(config: connectRequest.config),
|
||||||
"mnemonic": connectRequest.mnemonic,
|
"mnemonic": connectRequest.mnemonic,
|
||||||
"network": valueOf(network: connectRequest.network),
|
|
||||||
"dataDir": connectRequest.dataDir == nil ? nil : connectRequest.dataDir,
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,12 @@
|
|||||||
|
|
||||||
@interface RCT_EXTERN_MODULE(RNBreezLiquidSDK, RCTEventEmitter)
|
@interface RCT_EXTERN_MODULE(RNBreezLiquidSDK, RCTEventEmitter)
|
||||||
|
|
||||||
|
RCT_EXTERN_METHOD(
|
||||||
|
defaultConfig: (NSString*)network
|
||||||
|
resolve: (RCTPromiseResolveBlock)resolve
|
||||||
|
reject: (RCTPromiseRejectBlock)reject
|
||||||
|
)
|
||||||
|
|
||||||
RCT_EXTERN_METHOD(
|
RCT_EXTERN_METHOD(
|
||||||
parseInvoice: (NSString*)invoice
|
parseInvoice: (NSString*)invoice
|
||||||
resolve: (RCTPromiseResolveBlock)resolve
|
resolve: (RCTPromiseResolveBlock)resolve
|
||||||
|
|||||||
@@ -11,10 +11,15 @@ class RNBreezLiquidSDK: RCTEventEmitter {
|
|||||||
|
|
||||||
private var bindingLiquidSdk: BindingLiquidSdk!
|
private var bindingLiquidSdk: BindingLiquidSdk!
|
||||||
|
|
||||||
static var defaultDataDir: URL {
|
static var breezLiquidSdkDirectory: URL {
|
||||||
let applicationDirectory = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!
|
let applicationDirectory = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!
|
||||||
|
let breezLiquidSdkDirectory = applicationDirectory.appendingPathComponent("breezLiquidSdk", isDirectory: true)
|
||||||
|
|
||||||
return applicationDirectory.appendingPathComponent("breezLiquidSdk", isDirectory: true)
|
if !FileManager.default.fileExists(atPath: breezLiquidSdkDirectory.path) {
|
||||||
|
try! FileManager.default.createDirectory(atPath: breezLiquidSdkDirectory.path, withIntermediateDirectories: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return breezLiquidSdkDirectory
|
||||||
}
|
}
|
||||||
|
|
||||||
override init() {
|
override init() {
|
||||||
@@ -56,6 +61,28 @@ class RNBreezLiquidSDK: RCTEventEmitter {
|
|||||||
throw LiquidSdkError.Generic(message: "Not initialized")
|
throw LiquidSdkError.Generic(message: "Not initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func ensureWorkingDir(workingDir: String) throws {
|
||||||
|
do {
|
||||||
|
if !FileManager.default.fileExists(atPath: workingDir) {
|
||||||
|
try FileManager.default.createDirectory(atPath: workingDir, withIntermediateDirectories: true)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
throw LiquidSdkError.Generic(message: "Mandatory field workingDir must contain a writable directory")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc(defaultConfig:resolve:reject:)
|
||||||
|
func defaultConfig(_ network: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
||||||
|
do {
|
||||||
|
let networkTmp = try BreezLiquidSDKMapper.asNetwork(network: network)
|
||||||
|
var res = BreezLiquidSDK.defaultConfig(network: networkTmp)
|
||||||
|
res.workingDir = RNBreezLiquidSDK.breezLiquidSdkDirectory.path
|
||||||
|
resolve(BreezLiquidSDKMapper.dictionaryOf(config: res))
|
||||||
|
} catch let err {
|
||||||
|
rejectErr(err: err, reject: reject)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@objc(parseInvoice:resolve:reject:)
|
@objc(parseInvoice:resolve:reject:)
|
||||||
func parseInvoice(_ invoice: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
func parseInvoice(_ invoice: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
||||||
do {
|
do {
|
||||||
@@ -85,7 +112,8 @@ class RNBreezLiquidSDK: RCTEventEmitter {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
var connectRequest = try BreezLiquidSDKMapper.asConnectRequest(connectRequest: req)
|
var connectRequest = try BreezLiquidSDKMapper.asConnectRequest(connectRequest: req)
|
||||||
connectRequest.dataDir = connectRequest.dataDir == nil || connectRequest.dataDir!.isEmpty ? RNBreezLiquidSDK.defaultDataDir.path : connectRequest.dataDir
|
try ensureWorkingDir(workingDir: connectRequest.config.workingDir)
|
||||||
|
|
||||||
bindingLiquidSdk = try BreezLiquidSDK.connect(req: connectRequest)
|
bindingLiquidSdk = try BreezLiquidSDK.connect(req: connectRequest)
|
||||||
resolve(["status": "ok"])
|
resolve(["status": "ok"])
|
||||||
} catch let err {
|
} catch let err {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { NativeModules, Platform, NativeEventEmitter } from "react-native"
|
import { NativeModules, Platform, EmitterSubscription, NativeEventEmitter } from "react-native"
|
||||||
|
|
||||||
const LINKING_ERROR =
|
const LINKING_ERROR =
|
||||||
`The package 'react-native-breez-liquid-sdk' doesn't seem to be linked. Make sure: \n\n` +
|
`The package 'react-native-breez-liquid-sdk' doesn't seem to be linked. Make sure: \n\n` +
|
||||||
@@ -23,10 +23,17 @@ export interface BackupRequest {
|
|||||||
backupPath?: string
|
backupPath?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ConnectRequest {
|
export interface Config {
|
||||||
mnemonic: string
|
boltzUrl: string
|
||||||
|
electrumUrl: string
|
||||||
|
workingDir: string
|
||||||
network: Network
|
network: Network
|
||||||
dataDir?: string
|
paymentTimeoutSec: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ConnectRequest {
|
||||||
|
config: Config
|
||||||
|
mnemonic: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GetInfoRequest {
|
export interface GetInfoRequest {
|
||||||
@@ -193,6 +200,12 @@ export const setLogger = async (logger: Logger): Promise<EmitterSubscription> =>
|
|||||||
|
|
||||||
return subscription
|
return subscription
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const defaultConfig = async (network: Network): Promise<Config> => {
|
||||||
|
const response = await BreezLiquidSDK.defaultConfig(network)
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
export const parseInvoice = async (invoice: string): Promise<LnInvoice> => {
|
export const parseInvoice = async (invoice: string): Promise<LnInvoice> => {
|
||||||
const response = await BreezLiquidSDK.parseInvoice(invoice)
|
const response = await BreezLiquidSDK.parseInvoice(invoice)
|
||||||
return response
|
return response
|
||||||
|
|||||||
Reference in New Issue
Block a user