Merge branch 'connect-api' into go-examples

# Conflicts:
#	src/guide/getting_started.md
#	src/guide/lnurl_auth.md
#	src/guide/lnurl_pay.md
#	src/guide/lnurl_withdraw.md
#	src/guide/payments.md
#	src/guide/receive_onchain.md
#	src/guide/send_onchain.md
This commit is contained in:
Ross Savage
2023-07-11 09:32:20 +02:00
9 changed files with 573 additions and 130 deletions

View File

@@ -5,4 +5,14 @@ The SDK docs are live at [https://sdk-doc.breez.technology](https://sdk-doc.bree
## Contributions ## Contributions
For syntax and supported features, see [https://rust-lang.github.io/mdBook](https://rust-lang.github.io/mdBook). 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
```

View File

@@ -9,7 +9,7 @@ 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.) * 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 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 ## Installing
@@ -19,44 +19,30 @@ Breez SDK is available in several platforms. Follow the [Installing](install.md)
<div slot="title">Rust</div> <div slot="title">Rust</div>
<section> <section>
The first step is to register a new node. In order to do that a seed is needed. ## Connecting
## Registering a new node
```rust,no_run
let seed = <your seed>;
let invite_code = <your greenlight invite code>;
// 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?;
```
## Recovering an existing node
```rust,no_run
let seed = <your seed>;
let credentials = BreezServices::recover_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 ```rust,no_run
// Create the default config // Create the default config
let mut config = BreezServices::default_config(EnvironmentType::Production); let config = BreezServices::default_config(
EnvironmentType::Production,
"your API key".into(),
breez_sdk_core::NodeConfig::Greenlight {
config: GreenlightNodeConfig {
partner_credentials: None,
invite_code: None,
},
},
);
// Customize the config object according to your needs // 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(); 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, config,
seed.to_vec(), seed.to_vec(),
credentials.clone(),
Box::new(AppEventListener {}), Box::new(AppEventListener {}),
) )
.await?; .await?;
BreezServices::start(rt(), &sdk).await?;
``` ```
At any point we can fetch our balance from the Greenlight node: At any point we can fetch our balance from the Greenlight node:
@@ -71,35 +57,7 @@ if let Some(node_state) = sdk.node_info()? {
<div slot="title">Swift</div> <div slot="title">Swift</div>
<section> <section>
The first step is to register a new node ## Connecting
## Registering a new node
```swift
do {
let seed = try mnemonicToSeed(phrase: "<mnemonics words>")
let inviteCode = ""
// register_node takes either greenlight credentials (certifate & key) or invite code.
// At this example we are using the invite code option.
let credentials = try registerNode(network: Network.bitcoin, seed: seed, registerCredentials: nil, inviteCode: inviteCode)
} catch {
// handle error
}
```
## Recovering an existing node
```swift
do {
let seed = try mnemonicToSeed(phrase: "<mnemonics words>")
let credentials = try recoverNode(network: Network.bitcoin, seed: seed)
} catch {
// handle error
}
```
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
```swift ```swift
// SDK events listener // SDK events listener
@@ -110,15 +68,18 @@ class SDKListener: EventListener {
} }
// Create the default config // Create the default config
var config = defaultConfig(envType: EnvironmentType.production) 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 // Customize the config object according to your needs
config.apiKey = "your API key"; config.workingDir = "path to an existing directory"
config.workingDir = "path to an existing directory";
do { do {
let sdk = try initServices(config: config, seed: seed, creds: credentials, listener: SDKListener()); // Connect to the Breez SDK make it ready for use
try sdk.start(); let sdk = try connect(config: config, seed: seed, listener: SDKListener());
} catch{ } catch{
// handle error // handle error
} }
@@ -140,31 +101,7 @@ do {
<div slot="title">React Native</div> <div slot="title">React Native</div>
<section> <section>
The first step is to register a new node ## Connecting
## Registering a new node
```typescript
try {
const seed = await mnemonicToSeed("<mnemonics words>");
const inviteCode = "<your greenlight invite code>";
// register_node takes either greenlight credentials (certifate & key) or invite code.
// At this example we are using the invite code option.
const credentials = await registerNode(Network.BITCOIN, seed, null, inviteCode);
} catch (error) {
console.log(error)
}
```
## Recovering an existing node
```typescript
const seed = await mnemonicToSeed("<mnemonics words>");
const credentials = await recoverNode(Network.BITCOIN, seed);
```
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
```typescript ```typescript
// SDK events listener // SDK events listener
@@ -173,15 +110,22 @@ addEventListener((type, data) => {
}) })
// Create the default config // Create the default config
let config = defaultConfig(EnvironmentType.PRODUCTION) 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 // Customize the config object according to your needs
config.apiKey = "your API key";
config.workingDir = "path to an existing directory"; config.workingDir = "path to an existing directory";
try { try {
const sdkServices = await initServices(config, credentials.deviceKey, credentials.deviceCert, seed); // Connect to the Breez SDK make it ready for use
await start(); const sdkServices = await connect(config, seed);
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} }
@@ -199,32 +143,90 @@ try {
} }
``` ```
</section> </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: "", 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> <div slot="title">Go</div>
<section> <section>
The first step is to register a new node ## Connecting
## Registering a new node
```go
if seed, err := breez_sdk.MnemonicToSeed("<mnemonics words>"); err != nil {
inviteCode := "<your greenlight invite code>"
// register_node takes either greenlight credentials (certifate & key) or invite code.
// At this example we are using the invite code option.
credentials, err := breez_sdk.RegisterNode(breez_sdk.NetworkBitcoin, seed, nil, &inviteCode)
}
```
## Recovering an existing node
```go
if seed, err := breez_sdk.MnemonicToSeed("<mnemonics words>"); err != nil {
credentials := breez_sdk.RecoverNode(Network.BITCOIN, seed)
}
```
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
```go ```go
// SDK events listener // SDK events listener
type BreezListener struct{} type BreezListener struct{}
@@ -234,17 +236,24 @@ func (BreezListener) OnEvent(e breez_sdk.BreezEvent) {
} }
// Create the default config // Create the default config
config := breez_sdk.DefaultConfig(breez_sdk.EnvironmentTypeProduction) apiKey := "<your breez api key>"
inviteCode := "<your greenlight invite code>"
nodeConfig := breez_sdk.NodeConfigGreenlight{
Config: breez_sdk.GreenlightNodeConfig{
PartnerCredentials: nil,
InviteCode: &inviteCode,
},
}
config := breez_sdk.DefaultConfig(breez_sdk.EnvironmentTypeProduction, apiKey, nodeConfig)
// Customize the config object according to your needs // Customize the config object according to your needs
config.apiKey = "your API key"
config.workingDir = "path to an existing directory" config.workingDir = "path to an existing directory"
if sdkServices, err := breez_sdk.InitServices(config, seed, credentials, BreezListener{}); err != nil { if sdkServices, err := breez_sdk.Connect(config, seed, BreezListener{}); err != nil {
sdkServices.Start() sdkServices.Start()
} }
``` ```
At any point we can fetch our balance from the Greenlight node: At any point we can fetch our balance from the Greenlight node:
```go ```go

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: To do so, add the following to your Gradle dependencies:
``` groovy ```gradle
repositories { repositories {
maven { maven {
url("https://mvn.breez.technology/releases") url("https://mvn.breez.technology/releases")
@@ -50,4 +50,16 @@ Currently c# is built from source only. Please visit the [sdk-bindings](https://
## rust ## 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. 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 soruce. Please visit [sdk-core](https://github.com/breez/breez-sdk/tree/main/libs/sdk-bindings#python) project for instructions.

View File

@@ -73,6 +73,51 @@ try {
} }
``` ```
</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> </section>
<div slot="title">Go</div> <div slot="title">Go</div>
<section> <section>
@@ -93,11 +138,9 @@ if input, err := breez_sdk.ParseInput(lnurlAuthUrl); err != nil {
} }
} }
} }
}
``` ```
</section> </section>
</custom-tab> </custom-tabs>
## Supported Specs ## Supported Specs

View File

@@ -61,6 +61,48 @@ try {
} }
``` ```
</section> </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> <div slot="title">Go</div>
<section> <section>
@@ -83,7 +125,6 @@ if input, err := breez_sdk.ParseInput(lnurlPayUrl); err != nil {
</custom-tab> </custom-tab>
## Supported Specs ## Supported Specs
- [LUD-01](https://github.com/lnurl/luds/blob/luds/01.md) LNURL bech32 encoding - [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-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 - [LUD-16](https://github.com/lnurl/luds/blob/luds/16.md) LN Address

View File

@@ -61,6 +61,46 @@ try {
} }
``` ```
</section> </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> <div slot="title">Go</div>
<section> <section>

View File

@@ -94,6 +94,81 @@ try {
console.log(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> </section>
<div slot="title">Go</div> <div slot="title">Go</div>
<section> <section>
@@ -117,5 +192,6 @@ payment, err := sdkServices.SendPayment(bolt11, 3000)
const nodeId = "..."; const nodeId = "...";
payment, err := sdkServices.SendSpontaneousPayment(nodeId, 3000) payment, err := sdkServices.SendSpontaneousPayment(nodeId, 3000)
``` ```
</section> </section>
</custom-tabs> </custom-tabs>

View File

@@ -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? 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 ```rust,no_run
let destination_address = "...".into() 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 ```swift
let destinationAddress = "..." 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 ```typescript
const destinationAddress = "..." const destinationAddress = "..."
@@ -143,6 +143,109 @@ try {
} }
``` ```
</section> </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> <div slot="title">Go</div>
<section> <section>

View File

@@ -155,6 +155,115 @@ try {
} }
``` ```
</section> </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> <div slot="title">Go</div>
<section> <section>