From 4deeff9cfbc0134d1d86df807c28c4b864914103 Mon Sep 17 00:00:00 2001 From: Erdem Yerebasmaz Date: Tue, 4 Jul 2023 22:17:14 +0300 Subject: [PATCH] Add Dart examples --- src/guide/getting_started.md | 68 ++++++++++++++++++++++++++++++++++++ src/guide/install.md | 13 +++++-- src/guide/lnurl_auth.md | 24 +++++++++++++ src/guide/lnurl_pay.md | 24 +++++++++++++ src/guide/lnurl_withdraw.md | 23 ++++++++++++ src/guide/payments.md | 42 ++++++++++++++++++++++ src/guide/receive_onchain.md | 61 ++++++++++++++++++++++++++++++-- src/guide/send_onchain.md | 65 ++++++++++++++++++++++++++++++++-- 8 files changed, 313 insertions(+), 7 deletions(-) diff --git a/src/guide/getting_started.md b/src/guide/getting_started.md index 211a341..dc0fae8 100644 --- a/src/guide/getting_started.md +++ b/src/guide/getting_started.md @@ -199,6 +199,74 @@ try { } ``` +
Dart
+
+ +The first step is to register a new node. In order to do that a seed is needed. +## Registering a new node +```dart +try { + Config config = await defaultConfig(configType: EnvironmentType.Production); + Uint8List seed = await mnemonicToSeed(phrase: ""); + String inviteCode = ""; + + // registerNode takes either greenlight credentials (certifate & key) or invite code. + // At this example we are using the invite code option. + GreenlightCredentials credentials = await registerNode(network: Network.Bitcoin, seed: seed, config: config, inviteCode: inviteCode); +} catch (error) { + // handle error +} +``` + +## Recovering an existing node +```dart + Config config = await defaultConfig(configType: EnvironmentType.Production); + Uint8List seed = await mnemonicToSeed(""); + GreenlightCredentials credentials = await recoverNode(network: Network.Bitcoin, seed: seed, config: config); +``` + +Once the credentials are retrieved they should be saved in a secured storage. +The next step is to initialize the SDK and start the node: + +## Initializing the SDK +```dart +// SDK events listener +breezEventsStream().listen((event) { + print("Received Breez event: $event"); +} + +// SDK logs listener +breezLogStream().listen((log) { + print("Received Breez log entry: $log"); +} + +// Create the default config +Config config = await defaultConfig(configType: EnvironmentType.Production); + +// Customize the config object according to your needs +config.apiKey = "your API key"; +config.workingDir = "path to an existing directory"; + +try { + await initServices(config: config, seed: seed, creds: creds); + await startNode(); +} catch (error) { + // handle error +} +``` + +At any point we can fetch our balance from the Greenlight node: + +```dart +try { + NodeState? nodeInfo = await nodeInfo(); + int lnBalance = nodeInfo?.channelsBalanceMsat; + int onchainBalance = nodeInfo?.onchainBalanceMsat; +} catch (error) { + // handle error +} +``` +
You are now ready to receive a Lightning [payment](payments.md). diff --git a/src/guide/install.md b/src/guide/install.md index ec1f936..e8b7864 100644 --- a/src/guide/install.md +++ b/src/guide/install.md @@ -8,7 +8,7 @@ We recommend integrating the Breez SDK as Gradle dependency from [our Maven repo To do so, add the following to your Gradle dependencies: -``` groovy +```gradle repositories { maven { url("https://mvn.breez.technology/releases") @@ -42,4 +42,13 @@ Currently c# is built from source only. Please visit the [sdk-bindings](https:// ## rust -Currently rust is still not accessible via cargo and is needed to be built from source. Please visit the [sdk-core](https://github.com/breez/breez-sdk/tree/main/libs/sdk-core) project for instructions. \ No newline at end of file +Currently rust is still not accessible via cargo and is needed to be built from source. Please visit the [sdk-core](https://github.com/breez/breez-sdk/tree/main/libs/sdk-core) project for instructions. + +## Dart/Flutter +Currently Dart is built from source only. Please visit the [sdk-flutter](https://github.com/breez/breez-sdk/tree/main/libs/sdk-flutter#readme) project for instructions. We're planning to publish this package to [pub.dev](https://pub.dev/), until then it needs to be specified as a local directory dependency. + +```yaml +dependencies: + breez_sdk: + path: /breez-sdk/libs/sdk-flutter +``` \ No newline at end of file diff --git a/src/guide/lnurl_auth.md b/src/guide/lnurl_auth.md index 807ca30..533231d 100644 --- a/src/guide/lnurl_auth.md +++ b/src/guide/lnurl_auth.md @@ -73,6 +73,30 @@ try { } ``` + +
Dart
+
+ +```dart +// Endpoint can also be of the form: +// keyauth://domain.com/auth?key=val +String lnurlAuthUrl = "lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttvdankjm3lw3skw0tvdankjm3xdvcn6vtp8q6n2dfsx5mrjwtrxdjnqvtzv56rzcnyv3jrxv3sxqmkyenrvv6kve3exv6nqdtyv43nqcmzvdsnvdrzx33rsenxx5unqc3cxgeqgntfgu"; + +try { + InputType inputType = await parse(s: lnurlAuthUrl); + if (inputType is InputType_LnUrlAuth) { + LnUrlCallbackStatus result = await lnurlAuth(reqData: inputType.data); + if (result is LnUrlCallbackStatus_Ok) { + print("Successfully authenticated"); + } else { + print("Failed to authenticate"); + } + } +} catch (error) { + // handle error +} +``` +
diff --git a/src/guide/lnurl_pay.md b/src/guide/lnurl_pay.md index 9a80f44..469ac54 100644 --- a/src/guide/lnurl_pay.md +++ b/src/guide/lnurl_pay.md @@ -61,6 +61,30 @@ try { } ``` +
Dart
+
+ +```dart +// Endpoint can also be of the form: +// lnurlp://domain.com/lnurl-pay?key=val +// lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttsv9un7um9wdekjmmw84jxywf5x43rvv35xgmr2enrxanr2cfcvsmnwe3jxcukvde48qukgdec89snwde3vfjxvepjxpjnjvtpxd3kvdnxx5crxwpjvyunsephsz36jf +String lnurlPayUrl = "lightning@address.com"; + +try { + InputType inputType = await parseInput(s: lnurlPayUrl); + if (inputType is InputType_LnUrlPay) { + int amountSats = inputType.data.minSendable; + LnUrlCallbackStatus result = await lnurlPay( + reqData: inputType.data, + userAmountSat: amountSats, + comment: "comment", + ); + } +} catch (error) { + // handle error +} +``` +
## Supported Specs diff --git a/src/guide/lnurl_withdraw.md b/src/guide/lnurl_withdraw.md index 4c169aa..6ae9ee3 100644 --- a/src/guide/lnurl_withdraw.md +++ b/src/guide/lnurl_withdraw.md @@ -61,6 +61,29 @@ try { } ``` +
Dart
+
+ +```dart +// Endpoint can also be of the form: +// lnurlw://domain.com/lnurl-withdraw?key=val +String lnurlWithdrawUrl = "lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4exctthd96xserjv9mn7um9wdekjmmw843xxwpexdnxzen9vgunsvfexq6rvdecx93rgdmyxcuxverrvcursenpxvukzv3c8qunsdecx33nzwpnvg6ryc3hv93nzvecxgcxgwp3h33lxk"; + +try { + InputType inputType = await parseInput(s: lnurlWithdrawUrl); + if (inputType is InputType_LnUrlWithdraw) { + int amountSats = inputType.data.minWithdrawable; + LnUrlCallbackStatus result = await lnurlWithdraw( + reqData: inputType.data, + amountSats: amountSats, + description: "comment", + ); + } +} catch (error) { + // handle error +} +``` +
## Supported Specs diff --git a/src/guide/payments.md b/src/guide/payments.md index 061f6bf..b38b918 100644 --- a/src/guide/payments.md +++ b/src/guide/payments.md @@ -95,4 +95,46 @@ try { } ``` +
Dart
+
+ +## Receiving Lightning Payments +Breez SDK doesn't require you to open a channel and set up your inbound liquidity. +Breez SDK automatically connects your node to the LSP peer and you can now receive payments: + +```dart +try { + ReceivePaymentRequestData requestData = ReceivePaymentRequestData(amountSats: 3000, description: "Invoice for 3000 sats"); + ReceivePaymentResponse invoice = await receivePayment(reqData: requestData); +} catch (error) { + // handle error +} +``` + +## Sending Lightning Payments +```dart +String bolt11 = "..."; +try { + Payment payment = await sendPayment( + bolt11: bolt11, + amountSats: 3000, + ); +} catch (error) { + // handle error +} +``` + +## Sending Spontaneous Lightning Payments +```dart +String nodeId = "..."; +try { + Payment payment = await sendSpontaneousPayment( + nodeId: nodeId, + amountSats: 3000, + ); +} catch (error) { + // handle error +} +``` +
\ No newline at end of file diff --git a/src/guide/receive_onchain.md b/src/guide/receive_onchain.md index c0569eb..9024f01 100644 --- a/src/guide/receive_onchain.md +++ b/src/guide/receive_onchain.md @@ -29,7 +29,7 @@ In order to execute a refund, you need to supply an on-chain address to where th let refundables = sdk.list_refundables().await? ``` -Once you have a refundable swap in hand, use the follwing code to execute a refund: +Once you have a refundable swap in hand, use the following code to execute a refund: ```rust,no_run let destination_address = "...".into() @@ -76,7 +76,7 @@ do { } ``` -Once you have a refundable swap in hand, use the follwing code to execute a refund: +Once you have a refundable swap in hand, use the following code to execute a refund: ```swift let destinationAddress = "..." @@ -131,7 +131,7 @@ try { } ``` -Once you have a refundable swap in hand, use the follwing code to execute a refund: +Once you have a refundable swap in hand, use the following code to execute a refund: ```typescript const destinationAddress = "..." @@ -143,4 +143,59 @@ try { } ``` +
Dart
+
x + +```dart +try { + SwapInfo swapInfo = await receiveOnchain(); + + // Send your funds to the below bitcoin address + String address = swapInfo.bitcoinAddress; +} catch (error) { + // handle error +} +``` + +Once you've sent the funds to the above address, the SDK will monitor this address for unspent confirmed outputs and use a trustless submarine swap to receive these into your Lightning node. You can always monitor the status of the current in-progress swap using the following code: + +```dart +try { + SwapInfo? swapInfo = await inProgressSwap() +} catch (error) { + // handle error +} +``` + +The process of receiving funds via an on-chain address is trustless and uses a submarine swap. This means there are two ways to spend the sent funds: + +1. Either by a preimage that is exposed when the Lightning payment is completed - this is the positive case where the swap was successful. +2. Or by your node when the swap didn't complete within a certain timeout - this is the negative case where your node will execute a refund. + +In order to execute a refund, you need to supply an on-chain address to where the refunded amount will be sent. The following code will retrieve the refundable swaps: + +```dart +try { + List refundables = await listRefundables() +} catch (error) { + // handle error +} +``` + +Once you have a refundable swap in hand, use the following code to execute a refund: + +```dart +String destinationAddress = "..." +int satPerVbyte = +try { + String result = await refund( + swapAddress: refundable.bitcoinAddress, + toAddress: destinationAddress, + satPerVbyte: satPerVbyte, + ); +} catch (error) { + // handle error +} +``` +
\ No newline at end of file diff --git a/src/guide/send_onchain.md b/src/guide/send_onchain.md index 12367ee..378229a 100644 --- a/src/guide/send_onchain.md +++ b/src/guide/send_onchain.md @@ -121,8 +121,8 @@ of the total costs. Fetching the fees also tells you what is the range of amounts you can send: ```typescript -console.log(`Minimum amount, in sats: ${current_fees.min}`); -console.log(`Maximum amount, in sats: ${current_fees.max}`); +console.log(`Minimum amount, in sats: ${currentFees.min}`); +console.log(`Maximum amount, in sats: ${currentFees.max}`); ``` Once you checked the fees are acceptable, you can start the reverse swap: @@ -155,6 +155,67 @@ try { } ``` +
Dart
+
+ +```dart +try { + ReverseSwapPairInfo currentFees = await fetchReverseSwapFees(); + + print(`Percentage fee for the reverse swap service: ${currentFees.feesPercentage}`); + print(`Estimated miner fees in sats for locking up funds: ${currentFees.feesLockup}`); + print(`Estimated miner fees in sats for claiming funds: ${currentFees.feesClaim}`); +} catch (error) { + // handle error +} +``` + +The reverse swap will involve two on-chain transactions, for which the mining fees can only be estimated. They will happen +automatically once the process is started, but the last two values above are these estimates to help you get a picture +of the total costs. + +Fetching the fees also tells you what is the range of amounts you can send: + +```dart +print(`Minimum amount, in sats: ${currentFees.min}`); +print(`Maximum amount, in sats: ${currentFees.max}`); +``` + +Once you checked the fees are acceptable, you can start the reverse swap: + +```dart +String destinationAddress = "bc1.."; +int amountSat = currentFees.min; +int satPerVbyte = +try { + ReverseSwapInfo reverseSwapInfo = await sendOnchain( + amountSat: amountSat, + onchainRecipientAddress: destinationAddress, + pairHash: currentFees.feesHash, + satPerVbyte: satPerVbyte, + ); +} catch (error) { + // handle error +} +``` + +Starting the reverse swap will trigger a HODL invoice payment, which will only be settled if the entire swap completes. +This means you will see an outgoing pending payment in your list of payments, which locks those funds until the invoice +is either settled or cancelled. This will happen automatically at the end of the reverse swap. + +You can check its status with: + +```dart +try { + List swaps = await inProgressReverseSwaps(); + for (swap in swaps) { + print(`Reverse swap ${swap.id} in progress, status is ${swap.status}`); + } +} catch (error) { + // handle error +} +``` +
If the reverse swap is successful, you'll get the on-chain payment on your destination address and the HODL invoice will change from pending to settled.