Merge branch 'main' into swift-docs-update

This commit is contained in:
cnixbtc
2023-08-09 18:38:52 +02:00
17 changed files with 3973 additions and 77 deletions

View File

@@ -1 +1,18 @@
# Introduction
The SDK docs are live at [https://sdk-doc.breez.technology](https://sdk-doc.breez.technology).
## Contributions
For syntax and supported features, see [https://rust-lang.github.io/mdBook](https://rust-lang.github.io/mdBook).
## Develop
To locally serve the docs run:
```bash
cargo install mdbook
mdbook build
mdbook serve --open
```

View File

@@ -6,8 +6,10 @@ src = "src"
title = "Breez SDK"
[output.html]
theme="theme"
additional-js = ["tabs.js"]
git-repository-url = "https://github.com/breez/breez-sdk-docs"
edit-url-template = "https://github.com/breez/breez-sdk-docs/edit/main/{path}"
[output.html.print]
enable = false
enable = false

View File

@@ -1 +0,0 @@
# Introduction

View File

@@ -5,7 +5,7 @@
- [Installing](guide/install.md)
- [Getting Started](guide/getting_started.md)
- [Lightning payments](guide/payments.md)
- [Receiving an on-chain transaction](guide/recieve_onchain.md)
- [Receiving an on-chain transaction](guide/receive_onchain.md)
- [Sending an on-chain transaction](guide/send_onchain.md)
- [Connecting to an LSP](guide/connecting_lsp.md)
- [Using LNURL](guide/lnurl.md)

View File

@@ -1 +1,160 @@
# Connecting to an LSP
<custom-tabs category="lang">
<div slot="title">Swift</div>
<section>
Based on the API key provided to the Breez SDK, a default LSP is selected for your node to provide liquidity to it. To get the information about the selected LSP you can do the following:
```swift
do {
let lspId = try sdk.lspId()
let lspInfo = try sdk.fetchLspInfo(lspId: lspId!)
} catch {
// Handle error
}
```
When you have selected an LSP you may then connect to it.
```swift
do {
try sdk.connectLsp(lspId: lspId!)
} catch {
// Handle error
}
```
</section>
<div slot="title">React Native</div>
<section>
Based on the API key provided to the Breez SDK, a default LSP is selected for your node to provide liquidity to it. To get the information about the selected LSP you can do the following:
```typescript
try {
const lspId = await lspId()
const lspInfo = await fetchLspInfo(lspId)
} catch (error) {
console.log(error)
}
```
When you have selected an LSP you may then connect to it.
```typescript
try {
await connectLsp(lspId)
} catch (error) {
console.log(error)
}
```
</section>
<div slot="title">Dart</div>
<section>
Based on the API key provided to the Breez SDK, a default LSP is selected for your node to provide liquidity to it. To get the information about the selected LSP you can do the following:
```dart
try {
List<LspInformation> lspList = await listLsps();
// Select your desired LSP
} catch (e) {
// Handle error
}
```
When you have selected an LSP you may then connect to it.
```dart
try {
String lspId = await lspId();
await connectLSP(lspId);
} catch (e) {
// Handle error
}
```
</section>
<div slot="title">Python</div>
<section>
Based on the API key provided to the Breez SDK, a default LSP is selected for your node to provide liquidity to it. To get the information about the selected LSP you can do the following:
```python
try:
lsp_id = sdk_services.lsp_id()
lsp_info = sdk_services.fetch_lsp_info(lsp_id)
except Exception as error:
# Handle error
```
When you have selected an LSP you may then connect to it.
```python
try:
sdk_services.connect_lsp(lsp_id)
except Exception as error:
# Handle error
```
</section>
<div slot="title">Go</div>
<section>
Based on the API key provided to the Breez SDK, a default LSP is selected for your node to provide liquidity to it. To get the information about the selected LSP you can do the following:
```go
lspId, err := sdkServices.LspId()
if err != nil {
// Handle error
}
lspInfo, err := sdkServices.FetchLspInfo(*lspId)
if err != nil {
// Handle error
}
```
When you have selected an LSP you may then connect to it.
```go
err = sdkServices.ConnectLsp(*lspId)
if err != nil {
// Handle error
}
```
</section>
<div slot="title">C#</div>
<section>
Based on the API key provided to the Breez SDK, a default LSP is selected for your node to provide liquidity to it. To get the information about the selected LSP you can do the following:
```cs
try
{
var lspId = sdk.LspId();
var lspInfo = sdk.FetchLspInfo(lspId!);
}
catch (Exception)
{
// Handle error
}
```
When you have selected an LSP you may then connect to it.
```cs
try
{
sdk.ConnectLsp(lspId!);
}
catch (Exception)
{
// Handle error
}
```
</section>
</custom-tabs>

View File

@@ -1 +1,114 @@
# Supporting fiat currencies
# Supporting fiat currencies
<custom-tabs category="lang">
<div slot="title">React Native</div>
<section>
In order to list the availiable fiat currencies.
```typescript
try {
const fiatCurrencyList = await listFiatCurrencies()
} catch (error) {
console.log(error)
}
```
To get the current BTC rate for the currencies.
```typescript
try {
const fiatRatesMap = fetchFiatRates()
} catch (error) {
console.log(error)
}
```
</section>
<div slot="title">Dart</div>
<section>
In order to list the availiable fiat currencies.
```dart
try {
List<FiatCurrency> fiatCurrencyList = await listFiatCurrencies();
} catch(e) {
// Handle error
}
```
To get the current BTC rate for the currencies.
```dart
try {
Map<String, Rate> fiatRatesMap = fetchFiatRates();
// print your desired rate
print(fiatRatesMap["USD"]?.value);
} catch(e) {
// Handle error
}
```
</section>
<div slot="title">Python</div>
<section>
In order to list the availiable fiat currencies.
```python
try:
fiat_currencies = sdk_services.list_fiat_currencies()
except Exception as error:
#Handle error
```
To get the current BTC rate for the currencies.
```python
try:
fiat_rates = sdk_services.fetch_fiat_rates()
# print your desired rate
except Exception as error:
# Handle error
```
</section>
<div slot="title">Go</div>
<section>
In order to list the availiable fiat currencies.
```go
fiatCurrencies, err := sdkServices.ListFiatCurrencies()
```
To get the current BTC rate for the currencies.
```go
fiatRates, err := sdkServices.FetchFiatRates()
```
</section>
<div slot="title">C#</div>
<section>
In order to list the availiable fiat currencies.
```cs
try
{
var fiatCurrencies = sdk.ListFiatCurrencies();
}
catch (Exception)
{
// Handle error
}
```
To get the current BTC rate for the currencies.
```cs
try
{
var fiatRates = sdk.FetchFiatRates();
}
catch (Exception)
{
// Handle error
}
```
</section>
</custom-tabs>

View File

@@ -9,57 +9,331 @@ The Breez SDK provides the following services:
* Fetching node status (e.g. balance, max allow to pay, max allow to receive, on-chain balance, etc.)
* Connecting to a new or existing node.
Connecting to a node requires a seed (your master key) and credentials. The seed is a bip39 mnemonic and the credentials are retrieved by registering a new node or recovering an existing one.
Connecting to a node requires a seed (your master key). The seed is a bip39 mnemonic.
## Installing
Breez SDK is available in several platforms. Follow the [Installing](install.md) page for instructions on how to install on your platform.
## Registering a new node
```rust,no_run
let seed = <your seed>;
let invite_code = <your greenlight invite code>;
<custom-tabs category="lang">
<div slot="title">Rust</div>
<section>
// register_node takes either greenlight credentials (certifate & key) or invite code.
// At this example we are using the invite code option.
let credentials = BreezServices::register_node(Network::Bitcoin, seed, None, Some(invite_code)).await?;
```
## Connecting
```rust,ignore
let mnemonic = Mnemonic::generate_in(Language::English, 12)?;
let seed = mnemonic.to_seed("");
let invite_code = Some("...".into());
## Recovering an existing node
```rust,no_run
let seed = <your seed>;
let credentials = BreezServices::register_node(Network::Bitcoin, seed).await?;
```
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
```rust,no_run
// Create the default config
let config = BreezServices::default_config(EnvironmentType::Production)
let mut config = BreezServices::default_config(
EnvironmentType::Production,
"your API key".into(),
breez_sdk_core::NodeConfig::Greenlight {
config: GreenlightNodeConfig {
partner_credentials: None,
invite_code
},
},
);
// Customize the config object according to your needs
config.api_key = Some("your API key".into());
config.working_dir = "path to an existing directory".into();
let sdk = BreezServices::init_services(
// Connect to the Breez SDK make it ready for use
let sdk = BreezServices::connect(
config,
seed.to_vec(),
credentials.clone(),
seed.to_vec(),
Box::new(AppEventListener {}),
)
.await?;
BreezServices::start(rt(), &sdk).await?;
```
At any point we can fetch our balance from the Greenlight node:
```rust,no_run
```rust,ignore
if let Some(node_state) = sdk.node_info()? {
let balance_ln = node_state.channels_balance_msat;
let balance_onchain = node_state.onchain_balance_msat;
}
```
</section>
<div slot="title">Swift</div>
<section>
## Connecting
```swift
// SDK events listener
class SDKListener: EventListener {
func onEvent(e: BreezEvent) {
print("received event ", e)
}
}
// Create the default config
let seed = try mnemonicToSeed(phrase: "<mnemonics words>")
let inviteCode = "your invite code"
let config = breez_sdk.defaultConfig(envType: EnvironmentType.production, apiKey: "",
nodeConfig: NodeConfig.greenlight(
config: GreenlightNodeConfig(partnerCredentials: nil,inviteCode: inviteCode)));
// Customize the config object according to your needs
config.workingDir = "path to an existing directory"
do {
// Connect to the Breez SDK make it ready for use
let sdk = try connect(config: config, seed: seed, listener: SDKListener());
} catch{
// handle error
}
```
At any point we can fetch our balance from the Greenlight node:
```swift
do {
let nodeInfo = try sdk.nodeInfo()
let lnBalance = nodeInfo?.channelsBalanceMsat
let onchainBalance = nodeInfo?.onchainBalanceMsat
} catch {
// handle error
}
```
</section>
<div slot="title">React Native</div>
<section>
## Connecting
```typescript
// SDK events listener
addEventListener((type, data) => {
console.log(`received event ${type}`);
})
// Create the default config
const seed = await mnemonicToSeed("<mnemonics words>");
const inviteCode = "<your greenlight invite code>";
const nodeConfig : NodeConfig = {
type: NodeConfigType.GREENLIGHT,
config: {
inviteCode: "your invite code"
}
}
let config = defaultConfig(EnvironmentType.PRODUCTION, "api key", nodeConfig);
// Customize the config object according to your needs
config.workingDir = "path to an existing directory";
try {
// Connect to the Breez SDK make it ready for use
const sdkServices = await connect(config, seed);
} catch (error) {
console.log(error)
}
```
At any point we can fetch our balance from the Greenlight node:
```typescript
try {
const nodeInfo = await nodeInfo();
const lnBalance = nodeInfo.channelsBalanceMsat;
const onchainBalance = nodeInfo.onchainBalanceMsat;
} catch (error) {
console.log(error)
}
```
</section>
<div slot="title">Dart</div>
<section>
## Connecting
```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
Uint8List seed = await mnemonicToSeed(phrase: "<mnemonic words>");
String inviteCode = "<your greenlight invite code>";
NodeConfg nodeConfig = NodeConfig.greenlight(config: GreenlightNodeConfig(partnerCredentials: null, inviteCode: inviteCode));
Config config = await defaultConfig(configType: EnvironmentType.Production, apiKey: "<your API key>", nodeConfig: nodeConfig);
// Customize the config object according to your needs
config.workingDir = "path to an existing directory";
try {
// Connect to the Breez SDK make it ready for use
await connect(config: config, seed: seed);
} 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
}
```
</section>
<div slot="title">Python</div>
<section>
## Connecting
```python
# SDK events listener
class SDKListener(breez_sdk.EventListener):
def on_event(self, event):
print(event)
# Create the default config
seed = mnemonic_to_seed("<mnemonics words>")
invite_code = "<your greenlight invite code>"
config = breez_sdk.default_config(breez_sdk.EnvironmentType.PRODUCTION, "api key",
breez_sdk.NodeConfig.GREENLIGHT(breez_sdk.GreenlightNodeConfig(None, invite_code)))
# Customize the config object according to your needs
config.working_dir = "path to an existing directory"
try:
# Connect to the Breez SDK make it ready for use
sdk_services = breez_sdk.connect(config, seed, SDKListener())
except Exception as error:
# Handle error
```
At any point we can fetch our balance from the Greenlight node:
```python
try:
node_info = node_info()
ln_balance = node_info.channels_balance_msat
onchain_balance = node_info.onchain_balance_msat
except Exception as error:
# Handle error
```
</section>
<div slot="title">Go</div>
<section>
## Connecting
```go
// SDK events listener
type BreezListener struct{}
func (BreezListener) OnEvent(e breez_sdk.BreezEvent) {
log.Printf("received event %#v", e)
}
// Create the default config
seed, err := breez_sdk.MnemonicToSeed("<mnemonics words>")
if err != nil {
log.Fatalf("MnemonicToSeed failed: %#v", err)
}
apiKey := "<your breez api key>"
inviteCode := "<your greenlight invite code>"
nodeConfig := breez_sdk.NodeConfigGreenlight{
Config: breez_sdk.GreenlightNodeConfig{
PartnerCredentials: nil,
InviteCode: &inviteCode,
},
}
config, err := breez_sdk.DefaultConfig(breez_sdk.EnvironmentTypeProduction, apiKey, nodeConfig)
if err != nil {
log.Fatalf("DefaultConfig failed: %#v", err)
}
// Customize the config object according to your needs
config.workingDir = "path to an existing directory"
sdkServices, err := breez_sdk.Connect(config, seed, BreezListener{})
if err != nil {
log.Fatalf("Connect failed: %#v", err)
}
```
At any point we can fetch our balance from the Greenlight node:
```go
if nodeInfo, err := sdkServices.NodeInfo(); err != nil {
lnBalance := nodeInfo.ChannelsBalanceMsat
onchainBalance := nodeInfo.OnchainBalanceMsat
}
```
</section>
<div slot="title">C#</div>
<section>
## Connecting
```cs
using Breez.Sdk;
// Create the default config
var seed = BreezSdkMethods.MnemonicToSeed("<mnemonics words>");
var inviteCode = "<invite code>";
var apiKey = "<api key>";
var nodeConfig = new NodeConfig.Greenlight(
new GreenlightNodeConfig(null, inviteCode)
);
var config = BreezSdkMethods.DefaultConfig(
EnvironmentType.PRODUCTION,
apiKey,
nodeConfig
) with {
// Customize the config object according to your needs
workingDir = "path to an existing directory"
};
BlockingBreezServices sdk;
try
{
// Connect to the Breez SDK make it ready for use
sdk = BreezSdkMethods.Connect(config, seed, new SdkListener());
} catch (Exception)
{
// Handle error
}
// SDK event listener
class SdkListener : EventListener
{
public void OnEvent(BreezEvent e)
{
Console.WriteLine($"Received Breez event type {e.GetType().Name}");
}
}
```
At any point we can fetch our balance from the Greenlight node:
```cs
try
{
var nodeInfo = sdk.NodeInfo();
var lnBalance = nodeInfo?.channelsBalanceMsat;
var onchainBalance = nodeInfo?.onchainBalanceMsat;
}
catch (Exception) {
// Handle error
}
```
</section>
</custom-tabs>
You are now ready to receive a Lightning [payment](payments.md).

View File

@@ -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")
@@ -61,17 +61,41 @@ end
We recommmend using the official npm package:
```console
$ npm install @breeztech/react-native-breez-sdk
npm install https://github.com/breez/breez-sdk-react-native/releases/download/0.1.4/breeztech-react-native-breez-sdk-0.1.4.tgz
```
or
```console
$ yarn add @breeztech/react-native-breez-sdk
yarn add https://github.com/breez/breez-sdk-react-native/releases/download/0.1.4/breeztech-react-native-breez-sdk-0.1.4.tgz
```
## Go
We recommend using our official Go package: [breez/breez-sdk-go](https://github.com/breez/breez-sdk-go).
```console
go get github.com/breez/breez-sdk-go
```
## C#
Currently c# is built from source only. Please visit the [sdk-bindings](https://github.com/breez/breez-sdk/tree/main/libs/sdk-bindings#c) project for instructions.
We recommend using our official C# package: [Breez.Sdk](https://www.nuget.org/packages/Breez.Sdk).
```console
dotnet add package Breez.Sdk
```
## 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.
## 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: <relative-path-to>/breez-sdk/libs/sdk-flutter
```
## Python
Currently python is not acceable as a package and is needed to build it from source. Please visit [sdk-core](https://github.com/breez/breez-sdk/tree/main/libs/sdk-bindings#python) project for instructions.

View File

@@ -1,8 +1,11 @@
# LNURL-Auth
## Usage
<custom-tabs category="lang">
<div slot="title">Rust</div>
<section>
```rust,no_run
```rust,ignore
// Endpoint can also be of the form:
// keyauth://domain.com/auth?key=val
let lnurl_auth_url = "lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttvdankjm3lw3skw0tvdankjm3xdvcn6vtp8q6n2dfsx5mrjwtrxdjnqvtzv56rzcnyv3jrxv3sxqmkyenrvv6kve3exv6nqdtyv43nqcmzvdsnvdrzx33rsenxx5unqc3cxgeqgntfgu";
@@ -21,7 +24,150 @@ if let Ok(LnUrlAuth{data: ad}) = parse(lnurl_auth_url).await {
}
}
```
</section>
<div slot="title">Swift</div>
<section>
```swift
// Endpoint can also be of the form:
// keyauth://domain.com/auth?key=val
let lnurlAuthUrl = "lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttvdankjm3lw3skw0tvdankjm3xdvcn6vtp8q6n2dfsx5mrjwtrxdjnqvtzv56rzcnyv3jrxv3sxqmkyenrvv6kve3exv6nqdtyv43nqcmzvdsnvdrzx33rsenxx5unqc3cxgeqgntfgu"
do {
let inputType = try parseInput(s: lnurlAuthUrl)
if case .lnUrlAuth(let data) = inputType {
let result = try sdk.lnurlAuth(reqData: data)
switch result {
case .ok:
print("Successfully authenticated")
case .errorStatus(let data):
print("Failed to authenticate")
}
}
} catch {
// handle error
}
```
</section>
<div slot="title">React Native</div>
<section>
```typescript
// Endpoint can also be of the form:
// keyauth://domain.com/auth?key=val
let lnurlAuthUrl = "lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttvdankjm3lw3skw0tvdankjm3xdvcn6vtp8q6n2dfsx5mrjwtrxdjnqvtzv56rzcnyv3jrxv3sxqmkyenrvv6kve3exv6nqdtyv43nqcmzvdsnvdrzx33rsenxx5unqc3cxgeqgntfgu";
try {
const input = await parseInput(lnurlAuthUrl)
if (input.type === InputType.LNURL_AUTH) {
const result = await lnurlAuth(input.data)
if (result.status === "ok") {
print("Successfully authenticated")
} else {
print("Failed to authenticate")
}
}
} catch (error) {
console.log(error)
}
```
</section>
<div slot="title">Dart</div>
<section>
```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
}
```
</section>
<div slot="title">Python</div>
<section>
```python
# Endpoint can also be of the form:
# keyauth://domain.com/auth?key=val
lnurlAuthUrl = "lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttvdankjm3lw3skw0tvdankjm3xdvcn6vtp8q6n2dfsx5mrjwtrxdjnqvtzv56rzcnyv3jrxv3sxqmkyenrvv6kve3exv6nqdtyv43nqcmzvdsnvdrzx33rsenxx5unqc3cxgeqgntfgu"
try:
parsed_input = breez_sdk.parse_input(lnurl_auth_url)
if isinstance(parsed_input, breez_sdk.InputType.LN_URL_AUTH):
result = sdk_services.lnurl_auth(parsed_input.data)
if result.is_ok():
print("Successfully authenticated")
else:
print("Failed to authenticate")
except Exception as error:
# Handle error
```
</section>
<div slot="title">Go</div>
<section>
```go
// Endpoint can also be of the form:
// keyauth://domain.com/auth?key=val
lnurlAuthUrl := "lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttvdankjm3lw3skw0tvdankjm3xdvcn6vtp8q6n2dfsx5mrjwtrxdjnqvtzv56rzcnyv3jrxv3sxqmkyenrvv6kve3exv6nqdtyv43nqcmzvdsnvdrzx33rsenxx5unqc3cxgeqgntfgu"
if input, err := breez_sdk.ParseInput(lnurlAuthUrl); err != nil {
switch inputType := input.(type) {
case breez_sdk.InputTypeLnUrlAuth:
if result, err := sdkServices.LnurlAuth(inputType.Data); err != nil {
if (result.Status === "ok") {
log.Printf("Successfully authenticated")
} else {
log.Printf("Failed to authenticate")
}
}
}
```
</section>
<div slot="title">C#</div>
<section>
```cs
// Endpoint can also be of the form:
// keyauth://domain.com/auth?key=val
var lnurlAuthUrl = "lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttvdankjm3lw3skw0tvdankjm3xdvcn6vtp8q6n2dfsx5mrjwtrxdjnqvtzv56rzcnyv3jrxv3sxqmkyenrvv6kve3exv6nqdtyv43nqcmzvdsnvdrzx33rsenxx5unqc3cxgeqgntfgu";
try
{
var input = BreezSdkMethods.ParseInput(lnurlAuthUrl);
if (input is InputType.LnUrlAuth lnurla) {
var result = sdk.LnurlAuth(lnurla.data);
if (result is LnUrlCallbackStatus.Ok) {
Console.WriteLine("Successfully authenticated");
} else {
Console.WriteLine("Failed to authenticate");
}
}
}
catch (Exception)
{
// Handle error
}
```
</section>
</custom-tabs>
## Supported Specs

View File

@@ -2,7 +2,11 @@
## Usage
```rust,no_run
<custom-tabs category="lang">
<div slot="title">Rust</div>
<section>
```rust,ignore
// Endpoint can also be of the form:
// lnurlp://domain.com/lnurl-pay?key=val
// lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttsv9un7um9wdekjmmw84jxywf5x43rvv35xgmr2enrxanr2cfcvsmnwe3jxcukvde48qukgdec89snwde3vfjxvepjxpjnjvtpxd3kvdnxx5crxwpjvyunsephsz36jf
@@ -17,9 +21,133 @@ if let Ok(LnUrlPay{data: pd}) = parse(lnurl_pay_url).await {
}
```
</section>
<div slot="title">Swift</div>
<section>
```swift
// Endpoint can also be of the form:
// lnurlp://domain.com/lnurl-pay?key=val
// lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttsv9un7um9wdekjmmw84jxywf5x43rvv35xgmr2enrxanr2cfcvsmnwe3jxcukvde48qukgdec89snwde3vfjxvepjxpjnjvtpxd3kvdnxx5crxwpjvyunsephsz36jf
let lnurlPayUrl = "lightning@address.com";
do {
let inputType = try parseInput(s: lnurlPayUrl)
if case .lnUrlPay(let data) = inputType {
let amountSats = data.minSendable;
try sdk.payLnurl(reqData: data, amountSats: amountSats, comment: "comment")
}
} catch {
// handle error
}
```
</section>
<div slot="title">React Native</div>
<section>
```typescript
// Endpoint can also be of the form:
// lnurlp://domain.com/lnurl-pay?key=val
// lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttsv9un7um9wdekjmmw84jxywf5x43rvv35xgmr2enrxanr2cfcvsmnwe3jxcukvde48qukgdec89snwde3vfjxvepjxpjnjvtpxd3kvdnxx5crxwpjvyunsephsz36jf
let lnurlPayUrl = "lightning@address.com";
try {
const input = await parseInput(lnurlPayUrl)
if (input.type === InputType.LNURL_PAY) {
const amountSats = input.data.minSendable;
const result = await payLnurl(input.data, amountSats, "comment")
}
} catch (error) {
console.log(error)
}
```
</section>
<div slot="title">Dart</div>
<section>
```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
}
```
</section>
<div slot="title">Python</div>
<section>
```python
# Endpoint can also be of the form:
# lnurlp://domain.com/lnurl-pay?key=val
# lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttsv9un7um9wdekjmmw84jxywf5x43rvv35xgmr2enrxanr2cfcvsmnwe3jxcukvde48qukgdec89snwde3vfjxvepjxpjnjvtpxd3kvdnxx5crxwpjvyunsephsz36jf
lnurl_pay_url = "lightning@address.com"
try:
parsed_input = breez_sdk.parse_input(lnurl_pay_url)
if isinstance(parsed_input, breez_sdk.InputType.LN_URL_PAY):
amount_sats = parsed_input.data.min_sendable
result = sdk_service.pay_lnurl(parsed_input.data, amount_sats, "comment")
except Exception as error:
# Handle error
```
</section>
<div slot="title">Go</div>
<section>
```go
// Endpoint can also be of the form:
// lnurlp://domain.com/lnurl-pay?key=val
// lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttsv9un7um9wdekjmmw84jxywf5x43rvv35xgmr2enrxanr2cfcvsmnwe3jxcukvde48qukgdec89snwde3vfjxvepjxpjnjvtpxd3kvdnxx5crxwpjvyunsephsz36jf
lnurlPayUrl := "lightning@address.com"
if input, err := breez_sdk.ParseInput(lnurlPayUrl); err != nil {
switch inputType := input.(type) {
case breez_sdk.InputTypeLnUrlPay:
amountsSats := inputType.Data.MinSendable
comment := "comment"
result, err := sdkServices.PayLnurl(inputType.Data, amountsSats, &comment)
}
}
```
</section>
<div slot="title">C#</div>
<section>
```cs
// Endpoint can also be of the form:
// lnurlp://domain.com/lnurl-pay?key=val
// lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4excttsv9un7um9wdekjmmw84jxywf5x43rvv35xgmr2enrxanr2cfcvsmnwe3jxcukvde48qukgdec89snwde3vfjxvepjxpjnjvtpxd3kvdnxx5crxwpjvyunsephsz36jf
var lnurlPayUrl = "lightning@address.com";
try
{
var input = BreezSdkMethods.ParseInput(lnurlPayUrl);
if (input is InputType.LnUrlPay lnurlp) {
var amountSats = lnurlp.data.minSendable;
var result = sdk.PayLnurl(lnurlp.data, amountSats, "comment");
}
}
catch (Exception)
{
// Handle error
}
```
</section>
</custom-tabs>
## Supported Specs
- [LUD-01](https://github.com/lnurl/luds/blob/luds/01.md) LNURL bech32 encoding
- [LUD-06](https://github.com/lnurl/luds/blob/luds/06.md) `payRequest` spec
- [LUD-16](https://github.com/lnurl/luds/blob/luds/16.md) LN Address

View File

@@ -3,7 +3,11 @@
## Usage
```rust,no_run
<custom-tabs category="lang">
<div slot="title">Rust</div>
<section>
```rust,ignore
// Endpoint can also be of the form:
// lnurlw://domain.com/lnurl-withdraw?key=val
let lnurl_withdraw_url = "lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4exctthd96xserjv9mn7um9wdekjmmw843xxwpexdnxzen9vgunsvfexq6rvdecx93rgdmyxcuxverrvcursenpxvukzv3c8qunsdecx33nzwpnvg6ryc3hv93nzvecxgcxgwp3h33lxk";
@@ -16,7 +20,130 @@ if let Ok(LnUrlWithdraw{data: wd}) = parse(lnurl_withdraw_url).await {
sdk.lnurl_withdraw(wd, amount_msat, Some(description)).await?;
}
```
</section>
<div slot="title">Swift</div>
<section>
```swift
// Endpoint can also be of the form:
// lnurlw://domain.com/lnurl-withdraw?key=val
let lnurlWithdrawUrl = "lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4exctthd96xserjv9mn7um9wdekjmmw843xxwpexdnxzen9vgunsvfexq6rvdecx93rgdmyxcuxverrvcursenpxvukzv3c8qunsdecx33nzwpnvg6ryc3hv93nzvecxgcxgwp3h33lxk"
do {
let inputType = try parseInput(s: lnurlWithdrawUrl)
if case .lnUrlWithdraw(let data) = inputType {
let amountSat = data.minWithdrawable
let description = "Test withdraw"
try sdk.withdrawLnurl(reqData: data, amountSats: amountSat, description: "comment")
}
} catch {
// handle error
}
```
</section>
<div slot="title">React Native</div>
<section>
```typescript
// Endpoint can also be of the form:
// lnurlw://domain.com/lnurl-withdraw?key=val
let lnurlWithdrawUrl = "lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4exctthd96xserjv9mn7um9wdekjmmw843xxwpexdnxzen9vgunsvfexq6rvdecx93rgdmyxcuxverrvcursenpxvukzv3c8qunsdecx33nzwpnvg6ryc3hv93nzvecxgcxgwp3h33lxk";
try {
const input = await parseInput(lnurlWithdrawUrl)
if (input.type === InputType.LNURL_WITHDRAW) {
const amountSats = input.data.minWithdrawable;
const result = await withdrawLnurl(input.data, amountSats, "comment")
}
} catch (error) {
console.log(error)
}
```
</section>
<div slot="title">Dart</div>
<section>
```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
}
```
</section>
<div slot="title">Python</div>
<section>
```python
# Endpoint can also be of the form:
# lnurlw://domain.com/lnurl-withdraw?key=val
lnurl_withdraw_url = "lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4exctthd96xserjv9mn7um9wdekjmmw843xxwpexdnxzen9vgunsvfexq6rvdecx93rgdmyxcuxverrvcursenpxvukzv3c8qunsdecx33nzwpnvg6ryc3hv93nzvecxgcxgwp3h33lxk"
try:
parsed_input = parse_input(lnurl_withdraw_url)
if isinstance(parsed_input, breez_sdk.InputType.LN_URL_WITHDRAW):
amount_sats = parsed_input.data.min_withdrawable
result = sdk_services.withdraw_lnurl(parsed_input.data, amount_sats, "comment")
except Exception as error:
# Handle error
```
</section>
<div slot="title">Go</div>
<section>
```go
// Endpoint can also be of the form:
// lnurlw://domain.com/lnurl-withdraw?key=val
lnurlWithdrawUrl := "lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4exctthd96xserjv9mn7um9wdekjmmw843xxwpexdnxzen9vgunsvfexq6rvdecx93rgdmyxcuxverrvcursenpxvukzv3c8qunsdecx33nzwpnvg6ryc3hv93nzvecxgcxgwp3h33lxk"
if input, err := breez_sdk.ParseInput(lnurlWithdrawUrl); err != nil {
switch inputType := input.(type) {
case breez_sdk.InputTypeLnUrlWithdraw:
amountsSats := inputType.Data.MinWithdrawable
description := "comment"
result, err := sdkServices.WithdrawLnurl(inputType.Data, amountsSats, &description)
}
}
```
</section>
<div slot="title">C#</div>
<section>
```cs
// Endpoint can also be of the form:
// lnurlw://domain.com/lnurl-withdraw?key=val
var lnurlWithdrawUrl = "lnurl1dp68gurn8ghj7mr0vdskc6r0wd6z7mrww4exctthd96xserjv9mn7um9wdekjmmw843xxwpexdnxzen9vgunsvfexq6rvdecx93rgdmyxcuxverrvcursenpxvukzv3c8qunsdecx33nzwpnvg6ryc3hv93nzvecxgcxgwp3h33lxk";
try
{
var lnurlWithdrawUrl = "";
var input = BreezSdkMethods.ParseInput(lnurlWithdrawUrl);
if (input is InputType.LnUrlWithdraw lnurlw)
{
var amountSats = lnurlw.data.minWithdrawable;
var result = sdk.WithdrawLnurl(lnurlw.data, amountSats, "comment");
}
}
catch (Exception)
{
// Handle error
}
```
</section>
</custom-tabs>
## Supported Specs

View File

@@ -1,22 +1,241 @@
# Sending and receiving Lightning payments
<custom-tabs category="lang">
<div slot="title">Rust</div>
<section>
## 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:
```rust,no_run
```rust,ignore
let invoice = sdk.receive_payment(3000, "Invoice for 3000 sats".into()).await?;
```
## Sending Lightning Payments
```rust,no_run
```rust,ignore
let bolt11 = "...";
sdk.send_payment(bolt11.into(), Some(3000)).await?;
```
## Sending Spontaneous Lightning Payments
```rust,no_run
```rust,ignore
let node_id = "...";
sdk.send_payment(node_id.into(), Some(3000)).await?;
```
</section>
<div slot="title">Swift</div>
<section>
## 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:
```swift
do {
let invoice = try sdk.receivePayment(amountSats: 3000, description: "Invoice for 3000 sats")
} catch {
// handle error
}
```
## Sending Lightning Payments
```swift
let bolt11 = "...";
do {
let payment = try sdk.sendPayment(bolt11: bolt11, amountSats: 3000)
} catch {
// handle error
}
```
## Sending Spontaneous Lightning Payments
```swift
let nodeId = "...";
do {
let payment = try sdk.sendSpontaneousPayment(nodeId: nodeId, amountSats: 3000)
} catch {
// handle error
}
```
</section>
<div slot="title">React Native</div>
<section>
## 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:
```typescript
try {
const invoice = await receivePayment(3000, "Invoice for 3000 sats")
} catch (error) {
console.log(error)
}
```
## Sending Lightning Payments
```typescript
const bolt11 = "...";
try {
const payment = await sendPayment(bolt11, 3000)
} catch (error) {
console.log(error)
}
```
## Sending Spontaneous Lightning Payments
```typescript
const nodeId = "...";
try {
const payment = await sendSpontaneousPayment(nodeId, 3000)
} catch (error) {
console.log(error)
}
```
</section>
<div slot="title">Dart</div>
<section>
## 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
}
```
</section>
<div slot="title">Python</div>
<section>
## 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:
```python
try:
invoice = sdk_services.receive_payment(3000, "Invoice for 3000 sats")
except Exception as error:
# Handle error
```
## Sending Lightning Payments
```python
bolt11 = "..."
try:
sdk_services.send_payment(bolt11, 3000)
except Exception as error:
# Handle error
```
## Sending Spontaneous Lightning Payments
```python
let node_id = "..."
try:
sdk_services.send_spontaneous_payment(node_id, 3000)
except Exception as error:
# Handle error
```
</section>
<div slot="title">Go</div>
<section>
## 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:
```go
invoice, err := sdkServices.ReceivePayment(3000, "Invoice for 3000 sats")
```
## Sending Lightning Payments
```go
const bolt11 = "...";
payment, err := sdkServices.SendPayment(bolt11, 3000)
```
## Sending Spontaneous Lightning Payments
```go
const nodeId = "...";
payment, err := sdkServices.SendSpontaneousPayment(nodeId, 3000)
```
</section>
<div slot="title">C#</div>
<section>
## 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:
```cs
try
{
var invoice = sdk.ReceivePayment(3000, "Invoice for 3000 sats");
}
catch (Exception)
{
// Handle error
}
```
## Sending Lightning Payments
```cs
const bolt11 = "...";
try
{
var payment = sdk.SendPayment(bolt11, 3000);
}
catch (Exception)
{
// Handle error
}
```
## Sending Spontaneous Lightning Payments
```cs
const nodeId = "...";
try
{
var payment = sdk.SendSpontaneousPayment(nodeId, 3000);
}
catch (Exception)
{
// Handle error
}
```
</section>
</custom-tabs>

View File

@@ -0,0 +1,723 @@
# Receiving an on-chain transaction (swap-in)
There are cases when you have funds in some bitcoin address and you would like to send those to your lightning node.
<custom-tabs category="lang">
<div slot="title">Rust</div>
<section>
```rust,ignore
let swap_info = sdk.receive_onchain().await?;
// Send your funds to the below bitcoin address
let address = swap_info.bitcoin_address;
```
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:
```rust,ignore
let swap_info = sdk.in_progress_swap().await?
```
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:
```rust,ignore
let refundables = sdk.list_refundables().await?
```
Once you have a refundable swap in hand, use the following code to execute a refund:
```rust,ignore
let destination_address = "...".into()
let sat_per_vbyte = <refund tx fee rate>
sdk.refund(refundable.bitcoin_address, destination_address, sat_per_vbyte).await?
```
</section>
<div slot="title">Swift</div>
<section>
```swift
do {
let swapInfo = try sdk.receiveOnchain()
// Send your funds to the bellow bitcoin address
let address = swapInfo.bitcoinAddress;
} catch {
// 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:
```swift
do {
let swapInfo = try sdk.inProgressSwap()
} catch {
// 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:
```swift
do {
let refundables = try sdk.listRefundables()
} catch {
// handle error
}
```
Once you have a refundable swap in hand, use the following code to execute a refund:
```swift
let destinationAddress = "..."
let satPerVbyte = <refund tx fee rate>
do {
try sdk.refund(
swapAddress: "",
toAddress: destinationAddress,
satPerVbyte: satPerVbyte)
} catch {
// handle error
}
```
</section>
<div slot="title">React Native</div>
<section>
```typescript
try {
const swapInfo = await receiveOnchain()
// Send your funds to the below bitcoin address
const address = swapInfo.bitcoinAddress;
} catch (error) {
console.log(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:
```typescript
try {
const swapInfo = await inProgressSwap()
} catch (error) {
console.log(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:
```typescript
try {
const refundables = await listRefundables()
} catch (error) {
console.log(error)
}
```
Once you have a refundable swap in hand, use the following code to execute a refund:
```typescript
const destinationAddress = "..."
const satPerVbyte = <refund tx fee rate>
try {
const result = await refund(refundable.bitcoinAddress, destinationAddress, satPerVbyte)
} catch (error) {
console.log(error)
}
```
</section>
<div slot="title">Dart</div>
<section>
```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<SwapInfo> 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 = <refund tx fee rate>
try {
String result = await refund(
swapAddress: refundable.bitcoinAddress,
toAddress: destinationAddress,
satPerVbyte: satPerVbyte,
);
} catch (error) {
// handle error
}
```
</section>
<div slot="title">Python</div>
<section>
```python
try:
swap_info = sdk_services.receive_onchain()
# Send your funds to the below bitcoin address
address = sdk_services.swap_info.bitcoin_address
except Exception as 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:
```python
try:
swap_info = sdk_services.in_progress_swap()
except Exception as 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:
Once you have a refundable swap in hand, use the following code to execute a refund:
```python
try:
refundables = sdk_services.list_refundables()
except Exception as error:
# Handle error
```
Once you have a refundable swap in hand, use the follwing code to execute a refund:
```python
destination_address = "..."
sat_per_vbyte = <refund tx fee rate>
try:
sdk_services.refund(refundable.bitcoin_address, destination_address, sat_per_vbyte)
except Exception as error:
# Handle error
```
</section>
<div slot="title">Go</div>
<section>
```go
if swapInfo, err := sdkServices.ReceiveOnchain(); err != nil {
// Send your funds to the below bitcoin address
address := swapInfo.BitcoinAddress
}
```
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:
```go
swapInfo, err := sdkServices.InProgressSwap()
```
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:
```go
refundables, err := sdkServices.ListRefundables()
```
Once you have a refundable swap in hand, use the follwing code to execute a refund:
```go
destinationAddress := "..."
satPerVbyte := <refund tx fee rate>
result, err := sdkServices.Refund(refundable.BitcoinAddress, destinationAddress, satPerVbyte)
```
</section>
<div slot="title">C#</div>
<section>
```cs
try
{
var swapInfo = sdk.ReceiveOnchain();
// Send your funds to the below bitcoin address
var address = swapInfo.bitcoinAddress;
}
catch (Exception)
{
// 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:
```cs
try
{
var swapInfo = sdk.InProgressSwap();
}
catch (Exception)
{
// 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:
```cs
try
{
var refundables = sdk.ListRefundables();
}
catch (Exception)
{
// Handle error
}
```
Once you have a refundable swap in hand, use the following code to execute a refund:
```cs
var destinationAddress = "...";
var satPerVbyte = <refund tx fee rate>;
try
{
var result = sdk.Refund(refundable.bitcoinAddress, destinationAddress, satPerVbyte);
}
catch (Exception)
{
// Handle error
}
```
</section>
</custom-tabs>
# Calculating fees
<custom-tabs category="lang">
<div slot="title">Rust</div>
<section>
When the amount to be received exceeds the inbound liquidity of the node, a new channel will be opened by the LSP in order for the node to receive it. This can checked by retrieving the NodeState from the SDK and comparing the inbound liquidity to the amount to be received. If the amount is greater or equal to the inbound liquidity, a new channel opening is required.
To calculate the fees for a channel being opened by the LSP:
```rust,ignore
async fn calculate_channel_opening_fee(amount_msat: u64) -> Result<u64> {
let channel_opening_fee_needed = is_channel_opening_fee_needed(amount_msat, sdk.clone())?;
match channel_opening_fee_needed {
true => calculate_fees_for_amount(amount_msat).await,
false => Ok(0),
}
}
```
How to detect if open channel fees are needed:
```rust,ignore
fn is_channel_opening_fee_needed(amount_msats: u64) -> Result<bool> {
let node_info = sdk.node_info()?.ok_or(anyhow!("No node info found"))?;
Ok(node_info.inbound_liquidity_msats <= amount_msats)
}
```
LSP fees are calculated in two ways, either by a minimum fee set by the LSP or by a fee per myriad calculated based on the amount being received. If the fee calculated from the fee per myriad is less than the minimum fee, the minimum fee is used.
This information can be retrieved for each LSP and then calculated:
```rust,ignore
async fn calculate_fees_for_amount(amount_msat: u64) -> Result<u64> {
let lsp_id = sdk.lsp_id().await?.ok_or(anyhow!("No lsp id found"))?;
let lsp_info = sdk
.fetch_lsp_info(lsp_id)
.await?
.ok_or(anyhow!("No lsp id found"))?;
// We calculate the dynamic fees in millisatoshis rounded to satoshis.
let channel_dynamic_fee_msat =
amount_msat * lsp_info.channel_fee_permyriad as u64 / 10_000 / 1000 * 1000;
let fee_msat = max(
lsp_info.channel_minimum_fee_msat as u64,
channel_dynamic_fee_msat,
)
Ok(fee_msat)
}
```
</section>
<div slot="title">Swift</div>
<section>
When the amount to be received exceeds the inbound liquidity of the node, a new channel will be opened by the LSP in order for the node to receive it. This can checked by retrieving the NodeState from the SDK and comparing the inbound liquidity to the amount to be received. If the amount is greater or equal to the inbound liquidity, a new channel opening is required.
To calculate the fees for a channel being opened by the LSP:
```swift
func calculateChannelOpeningFee(amountMsats: Int64) -> Int64? {
var channelOpeningFeeNeeded = isChannelOpeningFeeNeeded(amountMsats: amountMsats)
if channelOpeningFeeNeeded {
return calculateFeesForAmount(amountMsats: amountMsats)
}
return nil
}
```
How to detect if open channel fees are needed:
```swift
func isChannelOpeningFeeNeeded(amountMsats: Int64) -> Bool {
do {
let nodeInfo = try sdk.nodeInfo()
if let inboundLiquidityMsats = nodeInfo?.inboundLiquidityMsats {
return inboundLiquidityMsats <= amountMsats
}
} catch {
// Handle error
}
return false
}
```
LSP fees are calculated in two ways, either by a minimum fee set by the LSP or by a fee per myriad calculated based on the amount being received. If the fee calculated from the fee per myriad is less than the minimum fee, the minimum fee is used.
This information can be retrieved for each LSP and then calculated:
```swift
func calculateFeesForAmount(amountMsats: Int64) -> Int64? {
do {
if let lspId = try sdk.lspId() {
let lspInfo = try sdk.fetchLspInfo(lspId: lspId)
// We calculate the dynamic fees in millisatoshis rounded to satoshis.
let channelDynamicFeeMsat = amountMsats * lspInfo!.channelFeePermyriad / 10_000 / 1000 * 1000
let feeMsat = max(lspInfo!.channelMinimumFeeMsat, channelDynamicFeeMsat)
return feeMsat
}
} catch {
// Handle error
}
return nil
}
```
</section>
<div slot="title">React Native</div>
<section>
When the amount to be received exceeds the inbound liquidity of the node, a new channel will be opened by the LSP in order for the node to receive it. This can checked by retrieving the NodeState from the SDK and comparing the inbound liquidity to the amount to be received. If the amount is greater or equal to the inbound liquidity, a new channel opening is required.
To calculate the fees for a channel being opened by the LSP:
```typescript
const calculateChannelOpeningFee = async (amountMsats: number): number => {
const channelOpeningFeeNeeded = await isChannelOpeningFeeNeeded(amountMsats)
if (channelOpeningFeeNeeded) {
return calculateFeesForAmount(amountMsats)
}
return 0
}
```
How to detect if open channel fees are needed:
```typescript
const isChannelOpeningFeeNeeded = async (amountMsats: number): boolean => {
try {
const nodeInfo = await nodeInfo()
return nodeInfo.inboundLiquidityMsats <= amountMsats
} catch (error) {
// handle error
}
return false
}
```
LSP fees are calculated in two ways, either by a minimum fee set by the LSP or by a fee per myriad calculated based on the amount being received. If the fee calculated from the fee per myriad is less than the minimum fee, the minimum fee is used.
This information can be retrieved for each LSP and then calculated:
```typescript
const calculateFeesForAmount = async (amountMsats: number): number => {
try {
const id = await lspId()
const lspInfo = await fetchLspInfo(id)
// We calculate the dynamic fees in millisatoshis rounded to satoshis.
const channelDynamicFeeMsat = amountMsats * lspInfo.channelFeePermyriad / 10000 / 1000 * 1000
const feeMsat = Math.max(lspInfo.channelMinimumFeeMsat, channelDynamicFeeMsat)
return feeMsat
} catch (error) {
// handle error
}
return 0
}
```
</section>
<div slot="title">Dart</div>
<section>
When the amount to be received exceeds the inbound liquidity of the node, a new channel will be opened by the LSP in order for the node to receive it. This can checked by retrieving the NodeState from the SDK and comparing the inbound liquidity to the amount to be received. If the amount is greater or equal to the inbound liquidity, a new channel opening is required.
To calculate the fees for a channel being opened by the LSP:
```dart
int calculateChannelOpeningFee(int amountMsat) async {
bool isChannelOpeningFeeNeeded = await isChannelOpeningFeeNeeded(amountMsat);
return isChannelOpeningFeeNeeded ? calculateFeesForAmount(amountMsat) : 0;
}
```
How to detect if open channel fees are needed:
```dart
// Assumes nodeState isn't empty
bool isChannelOpeningFeeNeeded(int amountMsat) async {
NodeState? nodeState = await getNodeState();
return amountMsat >= nodeState.inboundLiquidityMsats;
}
```
LSP fees are calculated in two ways, either by a minimum fee set by the LSP or by a fee per myriad calculated based on the amount being received. If the fee calculated from the fee per myriad is less than the minimum fee, the minimum fee is used.
This information can be retrieved for each LSP and then calculated:
```dart
// Assumes lspId & lspInformation isn't empty
int calculateFeesForAmount(int amountMsat) async {
String? lspId = await getLspId();
LSPInformation? lspInformation = await fetchLspInfo(lspId);
// We calculate the dynamic fees in millisatoshis rounded to satoshis.
int channelFeesMsat = (amountMsat * lspInformation.channelFeePermyriad / 10000 / 1000 * 1000);
return max(channelFeesMsat, lspInformation.channelMinimumFeeMsat);
}
```
</section>
<div slot="title">Python</div>
<section>
When the amount to be received exceeds the inbound liquidity of the node, a new channel will be opened by the LSP in order for the node to receive it. This can checked by retrieving the NodeState from the SDK and comparing the inbound liquidity to the amount to be received. If the amount is greater or equal to the inbound liquidity, a new channel opening is required.
To calculate the fees for a channel being opened by the LSP:
```python
def calculate_channel_opening_fees(amount_msats):
is_channel_opening_fee_needed = is_channel_opening_fee_needed()
if is_channel_opening_fee_needed:
return calculate_fees_for_amount(amount_msats)
else:
return None
```
How to detect if open channel fees are needed:
```python
def is_channel_opening_fee_needed(amount_msats):
return sdk_services.node_info().inbound_liquidity_msats <= amount_msats
```
LSP fees are calculated in two ways, either by a minimum fee set by the LSP or by a fee per myriad calculated based on the amount being received. If the fee calculated from the fee per myriad is less than the minimum fee, the minimum fee is used.
This information can be retrieved for each LSP and then calculated:
```python
def calculate_fees_for_amount(amount_msats):
# We need to open channel so we are calculating the fees for the LSP.
lsp_id = sdk_services.lsp_id()
lsp_info = sdk_services.fetch_lsp_info()
# We calculate the dynamic fees in millisatoshis rounded to satoshis.
channel_dynamic_fee = amount_msats * lsp_info.channel_minimum_fee_msat * lsp_info.channel_fee_permyriad / 10000 // 10000 * 10000
fee_msat = max(lsp_info.channel_minimum_fee_msat, channel_dynamic_fee)
return fee_msat
```
</section>
<div slot="title">Go</div>
<section>
When the amount to be received exceeds the inbound liquidity of the node, a new channel will be opened by the LSP in order for the node to receive it. This can checked by retrieving the NodeState from the SDK and comparing the inbound liquidity to the amount to be received. If the amount is greater or equal to the inbound liquidity, a new channel opening is required.
To calculate the fees for a channel being opened by the LSP:
```go
func CalculateChannelOpeningFee(amountMsats uint64) (uint64, error) {
isChannelOpeningFeeNeeded := isChannelOpeningFeeNeeded(amountMsats)
if !isChannelOpeningFeeNeeded {
return 0, fmt.Errorf("Channel not needed")
}
return calculateFeesForAmount(amountMsats), nil
}
```
How to detect if open channel fees are needed:
```go
func isChannelOpeningFeeNeeded(amountMsats uint64) bool {
nodeInfo, err := sdkServices.NodeInfo()
if err != nil {
// Handle error
}
return nodeInfo.InboundLiquidityMsats <= amountMsats
}
```
LSP fees are calculated in two ways, either by a minimum fee set by the LSP or by a fee per myriad calculated based on the amount being received. If the fee calculated from the fee per myriad is less than the minimum fee, the minimum fee is used.
This information can be retrieved for each LSP and then calculated:
```go
func calculateFeesForAmount(amountMsats uint64) uint64 {
lspId, err := sdkServices.LspId()
if err != nil {
// Handle error
}
lspInfo, err := sdkServices.FetchLspInfo(*lspId)
if err != nil {
// Handle error
}
// We calculate the dynamic fees in millisatoshis rounded to satoshis
channelDynamicFeeMsats := amountMsats * uint64(lspInfo.ChannelFeePermyriad) / 10000 / 1000 * 1000
feeMsats := uint64(lspInfo.ChannelMinimumFeeMsat)
if channelDynamicFeeMsats >= feeMsats {
feeMsats = channelDynamicFeeMsats
}
return feeMsats
}
```
</section>
<div slot="title">C#</div>
<section>
When the amount to be received exceeds the inbound liquidity of the node, a new channel will be opened by the LSP in order for the node to receive it. This can checked by retrieving the NodeState from the SDK and comparing the inbound liquidity to the amount to be received. If the amount is greater or equal to the inbound liquidity, a new channel opening is required.
To calculate the fees for a channel being opened by the LSP:
```cs
ulong calculateChannelOpeningFee(ulong amountMsats)
{
var channelOpeningFeeNeeded = isChannelOpeningFeeNeeded(amountMsats);
if (channelOpeningFeeNeeded)
{
return calculateFeesForAmount(amountMsats);
}
return 0;
}
```
How to detect if open channel fees are needed:
```typescript
bool isChannelOpeningFeeNeeded(ulong amountMsats)
{
try
{
var nodeInfo = sdk.NodeInfo();
return nodeInfo.inboundLiquidityMsats <= amountMsats;
}
catch (Exception)
{
// handle error
}
return false;
}
```
LSP fees are calculated in two ways, either by a minimum fee set by the LSP or by a fee per myriad calculated based on the amount being received. If the fee calculated from the fee per myriad is less than the minimum fee, the minimum fee is used.
This information can be retrieved for each LSP and then calculated:
```typescript
ulong calculateFeesForAmount(ulong amountMsats)
{
try
{
var id = sdk.LspId();
var lspInfo = sdk.FetchLspInfo(id);
// We calculate the dynamic fees in millisatoshis rounded to satoshis.
var channelDynamicFeeMsat = amountMsats * (ulong)lspInfo.channelFeePermyriad / 10000 / 1000 * 1000;
var feeMsat = Math.Max((ulong)lspInfo.channelMinimumFeeMsat, channelDynamicFeeMsat);
return feeMsat;
}
catch (Exception)
{
// Handle error
}
return 0;
}
```
</section>
</custom-tabs>

View File

@@ -1,34 +0,0 @@
# Receiving an on-chain transaction (swap-in)
There are cases when you have funds in some bitcoin address and you would like to send those to your lightning node.
```rust,no_run
let swap_info = sdk.receive_onchain().await?;
// Send your funds to the bellow bitcoin address
let address = swap_info.bitcoin_address;
```
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:
```rust,no_run
let swap_info = sdk.in_progress_swap().await?
```
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:
```rust,no_run
let refundables = sdk.list_refundables().await?
```
Once you have a refundable swap in hand, use the follwing code to execute a refund:
```rust,no_run
let destination_address = "...".into()
let sat_per_byte = <efund tx fee rate>
sdk.refund(refundable.bitcoin_address, destination_address, sat_per_byte).await?
```

View File

@@ -1 +1,382 @@
# Sending an on-chain transaction (swap-out)
You can send funds from the Breez SDK wallet to an on-chain address as follows.
First, fetch the current reverse swap fees:
<custom-tabs category="lang">
<div slot="title">Rust</div>
<section>
```rust,ignore
let current_fees = sdk.fetch_reverse_swap_fees().await?;
info!("Percentage fee for the reverse swap service: {}", current_fees.fees_percentage);
info!("Estimated miner fees in sats for locking up funds: {}", current_fees.fees_lockup);
info!("Estimated miner fees in sats for claiming funds: {}", current_fees.fees_claim);
```
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:
```rust,ignore
info!("Minimum amount, in sats: {}", current_fees.min);
info!("Maximum amount, in sats: {}", current_fees.max);
```
Once you checked the fees are acceptable, you can start the reverse swap:
```rust,ignore
let destination_address = String::from("bc1..");
let amount_sat = current_fees.min;
sdk.send_onchain(amount_sat, destination_address, current_fees.fees_hash).await?;
```
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:
```rust,ignore
for rs in sdk.in_progress_reverse_swaps().await? {
info!("Reverse swap {} in progress, status is {}", rs.id, rs.status);
}
```
</section>
<div slot="title">Swift</div>
<section>
```swift
try {
let currentFees = try sdk.fetchReverseSwapFees()
println("Percentage fee for the reverse swap service: \(currentFees.feesPercentage)")
println("Estimated miner fees in sats for locking up funds: \(currentFees.feesLockup)")
println("Estimated miner fees in sats for claiming funds: \(currentFees.feesClaim)")
} catch {
// 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:
```swift
println("Minimum amount, in sats: \(current_fees.min)")
println("Maximum amount, in sats: \(current_fees.max)")
```
Once you checked the fees are acceptable, you can start the reverse swap:
```swift
let destinationAddress = "bc1.."
let amountSat = currentFees.min
let satPerVbyte = <fee rate>
try {
try sdk.sendOnchain(amountSat: amountSat, onchainRecipientAddress: destinationAddress, pairHash: currentFees.feesHash, satPerVbyte: satPerVbyte)
} catch {
// 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:
```swift
for rs in sdk.inProgressReverseSwaps() {
println("Reverse swap \(rs.id) in progress, status is \(rs.status)")
}
```
</section>
<div slot="title">React Native</div>
<section>
```typescript
try {
const currentFees = await fetchReverseSwapFees()
console.log(`Percentage fee for the reverse swap service: ${currentFees.feesPercentage}`);
console.log(`Estimated miner fees in sats for locking up funds: ${currentFees.feesLockup}`);
console.log(`Estimated miner fees in sats for claiming funds: ${currentFees.feesClaim}`);
} catch (error) {
console.log(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:
```typescript
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:
```typescript
const destinationAddress = "bc1..";
const amountSat = currentFees.min;
const satPerVbyte = <fee rate>
try {
const reverseSwapInfo = sendOnchain(amountSat, destinationAddress, currentFees.feesHash, satPerVbyte)
} catch (error) {
console.log(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:
```typescript
try {
const swaps = await inProgressReverseSwaps()
for (const swap in swaps) {
println(`Reverse swap ${swap.id} in progress, status is ${swap.status}`);
}
} catch (error) {
console.log(error)
}
```
</section>
<div slot="title">Dart</div>
<section>
```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 = <fee rate>
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<ReverseSwapInfo> swaps = await inProgressReverseSwaps();
for (swap in swaps) {
print(`Reverse swap ${swap.id} in progress, status is ${swap.status}`);
}
} catch (error) {
// handle error
}
```
</section>
<div slot="title">Python</div>
<section>
```python
try:
current_fees = sdk_services.fetch_reverse_swap_fees()
print("Percentage fee for the reverse swap service: ", current_fees.fees_percentage)
print("Estimated miner fees in sats for locking up funds: ", current_fees.fees_lockup)
print("Estimated miner fees in sats for claiming funds: ", current_fees.fees_claim)
except Exception as 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:
```python
print("Minimum amount, in sats: ", current_fees.min)
print("Maximum amount, in sats: ", current_fees.max)
```
Once you checked the fees are acceptable, you can start the reverse swap:
```python
destination_address = "bc1.."
amount_sat = current_fees.min
try:
sdk.send_onchain(amount_sat, destination_address, current_fees.fees_hash)
except Exception as error:
# Handle erorr
```
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:
```python
try:
reverse_swaps = sdk_services.in_progress_reverse_swaps()
for rs in reverse_swaps:
print("Reverse swap ",rs.id , " in progress, status is ", rs.status)
except Exception as error:
# Handle erorr
```
</section>
<div slot="title">Go</div>
<section>
```go
if currentFees, err := sdkServices.FetchReverseSwapFees(); err != nil {
log.Printf("Percentage fee for the reverse swap service: %v", currentFees.FeesPercentage)
log.Printf("Estimated miner fees in sats for locking up funds: %v", currentFees.FeesLockup)
log.Printf("Estimated miner fees in sats for claiming funds: %v", currentFees.FeesClaim)
}
```
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:
```go
log.Printf("Minimum amount, in sats: %v", currentFees.Min)
log.Printf("Maximum amount, in sats: %v", currentFees.Max)
```
Once you checked the fees are acceptable, you can start the reverse swap:
```go
destinationAddress := "bc1.."
amountSat := currentFees.Min
satPerVbyte := <fee rate>
reverseSwapInfo, err := sdkServices.SendOnchain(amountSat, destinationAddress, currentFees.FeesHash, satPerVbyte)
```
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:
```go
if swaps, err := sdkServices.InProgressReverseSwaps(); err != nil {
for _, swap := range swaps {
log.Printf("Reverse swap %v in progress, status is %v", swap.Id, swap.Status)
}
}
```
</section>
<div slot="title">C#</div>
<section>
```cs
try
{
var currentFees = sdk.FetchReverseSwapFees();
Console.WriteLine($"Percentage fee for the reverse swap service: {currentFees.feesPercentage}");
Console.WriteLine($"Estimated miner fees in sats for locking up funds: {currentFees.feesLockup}");
Console.WriteLine($"Estimated miner fees in sats for claiming funds: {currentFees.feesClaim}");
}
catch (Exception)
{
// 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:
```cs
Console.WriteLine($"Minimum amount, in sats: {currentFees.min}");
Console.WriteLine($"Maximum amount, in sats: {currentFees.max}");
```
Once you checked the fees are acceptable, you can start the reverse swap:
```cs
var destinationAddress = "bc1..";
var amountSat = currentFees.min;
var satPerVbyte = <fee rate>;
try
{
var reverseSwapInfo = sdk.SendOnchain(amountSat, destinationAddress, currentFees.feesHash, satPerVbyte);
}
catch (Exception)
{
// 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:
```cs
try
{
var swaps = sdk.InProgressReverseSwaps();
foreach (var swap in swaps) {
Console.WriteLine($"Reverse swap {swap.id} in progress, status is {swap.status}`");
}
}
catch (Exception)
{
// Handle error
}
```
</section>
</custom-tabs>
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.
If however something goes wrong at any point in the process, the initial HODL payment will be cancelled and the funds in
your Breez SDK wallet will be unlocked.

147
tabs.js Normal file
View File

@@ -0,0 +1,147 @@
(function () {
'use strict';
let selected_ = null;
customElements.define('custom-tabs', class extends HTMLElement {
constructor() {
super(); // always call super() first in the ctor.
// Create shadow DOM for the component.
let shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<style>
:host {
display: inline-block;
contain: content;
border: 1px solid var(--quote-border);
border-radius: 8px;
width: 100%;
}
#panels {
padding: 10px;
}
#tabs {
border-bottom: 1px solid var(--quote-border);
background-color: var(--sidebar-bg);
}
#tabs slot {
display: inline-flex; /* Safari bug. Treats <slot> as a parent */
}
#tabs ::slotted(*) {
color: var(--sidebar-fg);
padding: 16px 8px;
margin: 0;
text-align: center;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
cursor: pointer;
border-top-left-radius: 8px;
border-top-right-radius: 3px;
border: none; /* if the user users a <button> */
}
#tabs ::slotted([tabindex="0"]), #tabs ::slotted(*:hover) {
color: var(--sidebar-active);
}
#panels ::slotted([aria-hidden="true"]) {
display: none;
}
pre {
margin: 0;
}
</style>
<div id="tabs">
<slot id="tabsSlot" name="title"></slot>
</div>
<div id="panels">
<slot id="panelsSlot"></slot>
</div>
`;
}
get selected() {
return selected_;
}
set selected(idx) {
selected_ = idx;
this._selectTab(idx);
this.setAttribute('selected', idx);
}
connectedCallback() {
this.setAttribute('role', 'tablist');
const tabsSlot = this.shadowRoot.querySelector('#tabsSlot');
const panelsSlot = this.shadowRoot.querySelector('#panelsSlot');
this.tabs = tabsSlot.assignedNodes({ flatten: true });
this.panels = panelsSlot.assignedNodes({ flatten: true }).filter(el => {
return el.nodeType === Node.ELEMENT_NODE;
});
// Save refer to we can remove listeners later.
this._boundOnTitleClick = this._onTitleClick.bind(this);
tabsSlot.addEventListener('click', this._boundOnTitleClick);
this.selected = this._findFirstSelectedTab() || this._findStoredSelectedTab() || 0;
}
disconnectedCallback() {
const tabsSlot = this.shadowRoot.querySelector('#tabsSlot');
tabsSlot.removeEventListener('click', this._boundOnTitleClick);
}
_onTitleClick(e) {
if (e.target.slot === 'title') {
this.selected = this.tabs.indexOf(e.target);
e.target.focus();
}
}
_findFirstSelectedTab() {
let selectedIdx;
for (let [i, tab] of this.tabs.entries()) {
tab.setAttribute('role', 'tab');
if (tab.hasAttribute('selected')) {
selectedIdx = i;
}
}
return selectedIdx;
}
_findStoredSelectedTab() {
let selectedIdx;
if (this.getAttribute("category")) {
let selectedText;
try { selectedText = localStorage.getItem('mdbook-tabs-' + this.getAttribute("category")); } catch (e) { }
if (selectedText) {
for (let [i, tab] of this.tabs.entries()) {
if (tab.textContent === selectedText) {
selectedIdx = i;
break;
}
}
}
}
return selectedIdx;
}
_selectTab(idx = null) {
for (let i = 0, tab; tab = this.tabs[i]; ++i) {
let select = i === idx;
tab.setAttribute('tabindex', select ? 0 : -1);
tab.setAttribute('aria-selected', select);
this.panels[i].setAttribute('aria-hidden', !select);
if (select && this.getAttribute("category") && tab.textContent) {
try { localStorage.setItem('mdbook-tabs-' + this.getAttribute("category"), tab.textContent); } catch (e) { }
}
}
}
});
})();

1471
theme/highlight.js Normal file

File diff suppressed because one or more lines are too long