Skip to content

Commit 4f1ecd9

Browse files
authored
Merge pull request #180 from guggero/docs-update
cmd/loop+README: explain fees and optimize fee display
2 parents 4a714e0 + 24066bb commit 4f1ecd9

File tree

4 files changed

+152
-31
lines changed

4 files changed

+152
-31
lines changed

README.md

Lines changed: 112 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ USAGE:
149149
150150
DESCRIPTION:
151151
152-
Attempts loop out the target amount into either the backing lnd's
152+
Attempts to loop out the target amount into either the backing lnd's
153153
wallet, or a targeted address.
154154
155155
The amount is to be specified in satoshis.
@@ -158,9 +158,12 @@ DESCRIPTION:
158158
specified. If not specified, a new wallet address will be generated.
159159
160160
OPTIONS:
161-
--channel value the 8-byte compact channel ID of the channel to loop out (default: 0)
162-
--addr value the optional address that the looped out funds should be sent to, if left blank the funds will go to lnd's wallet
163-
--amt value the amount in satoshis to loop out (default: 0)
161+
--channel value the 8-byte compact channel ID of the channel to loop out (default: 0)
162+
--addr value the optional address that the looped out funds should be sent to, if let blank the funds will go to lnd's wallet
163+
--amt value the amount in satoshis to loop out (default: 0)
164+
--conf_target value the number of blocks from the swap initiation height that the on-chain HTLC should be swept within (default: 6)
165+
--max_swap_routing_fee value the max off-chain swap routing fee in satoshis, if not specified, a default max fee will be used (default: 0)
166+
--fast Indicate you want to swap immediately, paying potentially a higher fee. If not set the swap server might choose to wait up to 30 minutes before publishing the swap HTLC on-chain, to save on its chain fees. Not setting this flag therefore might result in a lower swap fee.
164167
```
165168

166169
It's possible to receive more inbound capacity on a particular channel
@@ -178,6 +181,79 @@ swap is initiated successfully, `loopd` will see the process through.
178181

179182
To query in-flight swap statuses, run `loop monitor`.
180183

184+
### Fees explained
185+
186+
The following is an example output of a 0.01 BTC fast (non-batched) Loop Out
187+
swap from `testnet`:
188+
189+
```bash
190+
$ loop out --amt 1000000 --fast
191+
Max swap fees for 1000000 sat Loop Out: 36046 sat
192+
Fast swap requested.
193+
CONTINUE SWAP? (y/n), expand fee detail (x): x
194+
195+
Estimated on-chain sweep fee: 149 sat
196+
Max on-chain sweep fee: 14900 sat
197+
Max off-chain swap routing fee: 20010 sat
198+
Max off-chain prepay routing fee: 36 sat
199+
Max no show penalty (prepay): 1337 sat
200+
Max swap fee: 1100 sat
201+
CONTINUE SWAP? (y/n):
202+
```
203+
204+
Explanation:
205+
206+
- **Max swap fees for <x> sat Loop Out** (36046 sat): The absolute maximum in
207+
fees that need to be paid. This includes on-chain and off-chain fees. This
208+
represents the ceiling or worst-case scenario. The actual fees will likely be
209+
lower. This is the sum of `14900 + 20010 + 36 + 1100` (see below).
210+
- **Estimated on-chain sweep fee** (149 sat): The estimated cost to sweep the
211+
HTLC in case of success, calculated based on the _current_ on-chain fees.
212+
This value is called `miner_fee` in the gRPC/REST responses.
213+
- **Max on-chain sweep fee** (14900 sat): The maximum on-chain fee the daemon
214+
is going to allow for sweeping the HTLC in case of success. A fee estimation
215+
based on the `--conf_target` flag is always performed before sweeping. The
216+
factor of `100` times the estimated fee is applied in case the fees spike
217+
between the time the swap is initiated and the time the HTLC can be swept. But
218+
that is the absolute worst-case fee that will be paid. If there is no fee
219+
spike, a normal, much lower fee will be used.
220+
- **Max off-chain swap routing fee** (20010 sat): The maximum off-chain
221+
routing fee that the daemon should pay when finding a route to pay the
222+
Lightning invoice. This is a hard limit. If no route with a lower or equal fee
223+
is found, the payment (and the swap) is aborted. This value is calculated
224+
statically based on the swap amount (see `maxRoutingFeeBase` and
225+
`maxRoutingFeeRate` in `cmd/loop/main.go`).
226+
- **Max off-chain prepay routing fee** (36 sat): The maximum off-chain routing
227+
fee that the daemon should pay when finding a route to pay the prepay fee.
228+
This is a hard limit. If no route with a lower or equal fee is found, the
229+
payment (and the swap) is aborted. This value is calculated statically based
230+
on the prepay amount (see `maxRoutingFeeBase` and `maxRoutingFeeRate` in
231+
`cmd/loop/main.go`).
232+
- **Max no show penalty (prepay)** (1337 sat): This is the amount that has to be
233+
pre-paid (off-chain) before the server publishes the HTLC on-chain. This is
234+
necessary to ensure the server's on-chain fees are paid if the client aborts
235+
and never completes the swap _after_ the HTLC has been published on-chain.
236+
If the swap completes normally, this amount is counted towards the full swap
237+
amount and therefore is actually a pre-payment and not a fee. This value is
238+
called `prepay_amt` in the gRPC/REST responses.
239+
- **Max swap fee** (1100 sat): The maximum amount of service fees we allow the
240+
server to charge for executing the swap. The client aborts the swap if the
241+
fee proposed by the server exceeds this maximum. It is therefore recommended
242+
to obtain the maximum by asking the server for a quote first. The actual fees
243+
might be lower than this maximum if user specific discounts are applied. This
244+
value is called `swap_fee` in the gRPC/REST responses.
245+
246+
#### Fast vs. batched swaps
247+
248+
By default, Loop Outs are executed as normal speed swaps. This means the server
249+
will wait up to 30 minutes until it publishes the HTLC on-chain to improve the
250+
chances that it can be batched together with other user's swaps to reduce the
251+
on-chain footprint and fees. The server offers a reduced swap fee for slow swaps
252+
to incentivize users to batch more.
253+
254+
If a swap should be executed immediately, the `--fast` flag can be used. Fast
255+
swaps won't benefit from a reduced swap fee.
256+
181257
### Loop In Swaps
182258

183259
Additionally, Loop In is now also supported for mainnet as well. A Loop In swap
@@ -195,9 +271,10 @@ DESCRIPTION:
195271
Send the amount in satoshis specified by the amt argument off-chain.
196272
197273
OPTIONS:
198-
--amt value the amount in satoshis to loop in (default: 0)
199-
--external expect htlc to be published externally
200-
--conf_target the confirmation target for the on chain htlc, if not being published externally
274+
--amt value the amount in satoshis to loop in (default: 0)
275+
--external expect htlc to be published externally
276+
--conf_target value the target number of blocks the on-chain htlc broadcast by the swap client should confirm within (default: 0)
277+
--last_hop value the pubkey of the last hop to use for this swap
201278
```
202279

203280
The `--external` argument allows the on-chain HTLC transacting to be published
@@ -208,6 +285,34 @@ A Loop In swap can be executed a follows:
208285
```
209286
loop in <amt_in_satoshis>
210287
```
288+
289+
#### Fees explained
290+
291+
The following is an example output of a 0.01 BTC Loop In swap from `testnet`:
292+
293+
```bash
294+
$ loop in --amt 1000000
295+
Max swap fees for 1000000 sat Loop In: 1562 sat
296+
CONTINUE SWAP? (y/n), expand fee detail (x): x
297+
298+
Estimated on-chain HTLC fee: 154 sat
299+
Max swap fee: 1100 sat
300+
CONTINUE SWAP? (y/n):
301+
```
302+
303+
Explanation:
304+
305+
- **Estimated on-chain HTLC fee** (154 sat): The estimated on-chain fee that the
306+
daemon has to pay to publish the HTLC. This is an estimation from `lnd`'s
307+
wallet based on the available UTXOs and current network fees. This value is
308+
called `miner_fee` in the gRPC/REST responses.
309+
- **Max swap fee** (1100 sat): The maximum amount of service fees we allow the
310+
server to charge for executing the swap. The client aborts the swap if the
311+
fee proposed by the server exceeds this maximum. It is therefore recommended
312+
to obtain the maximum by asking the server for a quote first. The actual fees
313+
might be lower than this maximum if user specific discounts are applied. This
314+
value is called `swap_fee` in the gRPC/REST responses.
315+
211316
## Resume
212317

213318
When `loopd` is terminated (or killed) for whatever reason, it will pickup
@@ -220,4 +325,3 @@ Its location is `~/.loopd/<network>/loop.db`.
220325

221326
It is possible to execute multiple swaps simultaneously. Just keep loopd
222327
running.
223-

cmd/loop/loopin.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,10 @@ func loopIn(ctx *cli.Context) error {
119119
}
120120

121121
limits := getInLimits(amt, quote)
122-
err = displayLimits(swap.TypeIn, amt, limits, external, "")
122+
err = displayLimits(
123+
swap.TypeIn, amt, btcutil.Amount(quote.MinerFee), limits,
124+
external, "",
125+
)
123126
if err != nil {
124127
return err
125128
}

cmd/loop/loopout.go

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ var loopOutCommand = cli.Command{
1717
Usage: "perform an off-chain to on-chain swap (looping out)",
1818
ArgsUsage: "amt [addr]",
1919
Description: `
20-
Attempts loop out the target amount into either the backing lnd's
20+
Attempts to loop out the target amount into either the backing lnd's
2121
wallet, or a targeted address.
2222
2323
The amount is to be specified in satoshis.
@@ -26,8 +26,9 @@ var loopOutCommand = cli.Command{
2626
specified. If not specified, a new wallet address will be generated.`,
2727
Flags: []cli.Flag{
2828
cli.Uint64Flag{
29-
Name: "channel",
30-
Usage: "the 8-byte compact channel ID of the channel to loop out",
29+
Name: "channel",
30+
Usage: "the 8-byte compact channel ID of the channel " +
31+
"to loop out",
3132
},
3233
cli.StringFlag{
3334
Name: "addr",
@@ -48,18 +49,19 @@ var loopOutCommand = cli.Command{
4849
},
4950
cli.Int64Flag{
5051
Name: "max_swap_routing_fee",
51-
Usage: "the max off-chain swap routing fee in satoshis, " +
52-
"if let blank a default max fee will be used",
52+
Usage: "the max off-chain swap routing fee in " +
53+
"satoshis, if not specified, a default max " +
54+
"fee will be used",
5355
},
5456
cli.BoolFlag{
5557
Name: "fast",
5658
Usage: "Indicate you want to swap immediately, " +
5759
"paying potentially a higher fee. If not " +
5860
"set the swap server might choose to wait up " +
5961
"to 30 minutes before publishing the swap " +
60-
"HTLC on-chain, to save on chain fees. Not " +
61-
"setting this flag might result in a lower " +
62-
"swap fee.",
62+
"HTLC on-chain, to save on its chain fees. " +
63+
"Not setting this flag therefore might " +
64+
"result in a lower swap fee.",
6365
},
6466
},
6567
Action: loopOut,
@@ -135,7 +137,10 @@ func loopOut(ctx *cli.Context) error {
135137
ctx.Int64("max_swap_routing_fee"),
136138
)
137139
}
138-
err = displayLimits(swap.TypeOut, amt, limits, false, warning)
140+
err = displayLimits(
141+
swap.TypeOut, amt, btcutil.Amount(quote.MinerFee), limits,
142+
false, warning,
143+
)
139144
if err != nil {
140145
return err
141146
}

cmd/loop/main.go

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ func getLimits(amt btcutil.Amount, quote *looprpc.QuoteResponse) *limits {
141141
}
142142
}
143143

144-
func displayLimits(swapType swap.Type, amt btcutil.Amount, l *limits,
144+
func displayLimits(swapType swap.Type, amt, minerFees btcutil.Amount, l *limits,
145145
externalHtlc bool, warning string) error {
146146

147147
totalSuccessMax := l.maxMinerFee + l.maxSwapFee
@@ -159,7 +159,7 @@ func displayLimits(swapType swap.Type, amt btcutil.Amount, l *limits,
159159
"wallet.\n\n")
160160
}
161161

162-
fmt.Printf("Max swap fees for %d Loop %v: %d\n", amt, swapType,
162+
fmt.Printf("Max swap fees for %d sat Loop %v: %d sat\n", amt, swapType,
163163
totalSuccessMax)
164164

165165
if warning != "" {
@@ -176,26 +176,35 @@ func displayLimits(swapType swap.Type, amt btcutil.Amount, l *limits,
176176
return nil
177177
case "x":
178178
fmt.Println()
179-
if swapType != swap.TypeIn || !externalHtlc {
180-
fmt.Printf("Max on-chain fee: %d\n",
181-
l.maxMinerFee)
179+
f := "%-36s %d sat\n"
180+
181+
switch swapType {
182+
case swap.TypeOut:
183+
fmt.Printf(f, "Estimated on-chain sweep fee:",
184+
minerFees)
185+
fmt.Printf(f, "Max on-chain sweep fee:", l.maxMinerFee)
186+
187+
case swap.TypeIn:
188+
if !externalHtlc {
189+
fmt.Printf(f, "Estimated on-chain HTLC fee:",
190+
minerFees)
191+
}
182192
}
183193

184194
if l.maxSwapRoutingFee != nil {
185-
fmt.Printf("Max off-chain swap routing fee: %d\n",
195+
fmt.Printf(f, "Max off-chain swap routing fee:",
186196
*l.maxSwapRoutingFee)
187197
}
188198

189-
if l.maxPrepayRoutingFee != nil {
190-
fmt.Printf("Max off-chain prepay routing fee: %d\n",
191-
*l.maxPrepayRoutingFee)
192-
}
193-
fmt.Printf("Max swap fee: %d\n", l.maxSwapFee)
194-
195199
if l.maxPrepayAmt != nil {
196-
fmt.Printf("Max no show penalty: %d\n",
200+
fmt.Printf(f, "Max no show penalty (prepay):",
197201
*l.maxPrepayAmt)
198202
}
203+
if l.maxPrepayRoutingFee != nil {
204+
fmt.Printf(f, "Max off-chain prepay routing fee:",
205+
*l.maxPrepayRoutingFee)
206+
}
207+
fmt.Printf(f, "Max swap fee:", l.maxSwapFee)
199208

200209
fmt.Printf("CONTINUE SWAP? (y/n): ")
201210
fmt.Scanln(&answer)

0 commit comments

Comments
 (0)