Balancing with automatic fee adjustments

Posted about 2 years ago by BrunswickGanadoTutuilla

Hello, I'm relatively new to lightning and just learned about fee adjusting scripts. With these, you can set fees on your channels based on their local v.s. remote balance and consequently steer transactions through your node so that your channels remain balanced. 

What are my options?

You could write your own script, or you can use someone else's wheel. First I tried a nodejs version but the main install method is broken, and after I manually compiled it, it would time out during rebalancing. It also didn't give much for configuration. Then I tried another one suggested on a website, charge-lnd a python based script. It works excellently. There are options to run it from cron or under systemd. It provides many config options and you can classify channels. You can apply different rules based on many criteria like activity, size, balance, remote or local end amounts, or node-id. 

What should fees be?

When I tried to figure out what values to use, I surveyed other nodes with 1ml. You can see what the fee settings are on any lightning node. I have found four primary categories of fee rates (percentage) with one variation (base-fee).

The first is the "default" which is 1 mSat base and 1 mSat per kSat (0.0001% or one millionth of the transaction). This is the default channel policy of  LND and probably others. You aren't going to make much in fees with this even with very high volumes. These are usually newbies, but sometimes the big boys stick with the defaults like coingate.com. A variation on this theme is the 'fixed fee' configuration where regardless of what is on the other end of a channel, all channels will be given the same fee structure ala "lncli 1000 100 144".

The second is the "on and off ramp" nodes, the ones that provide a gateway to fiat like Strike, Cashapp, Bitrefill and the ones that host custodial wallets like WOS, Muun and Bluewallet's lndhub.io. These usually have base fee set to 1 Sat, but occasionally they are set to 10 Sats (like lndhub.io) . The "big boys" like zaphq.io, set their fees to zero between their own nodes like between lndus0.zaphq.io and lndeu1.zaphq.io but to "outsiders" they set their fees to 2250 mSat/kSat (0.225%). Bitrefill.com's policy appears to be 1000 m/k, and lndhub.io appears to vary between 10000 (1%) and 1000 (0.1%). Given the high profitibility of their channels, they might be able to afford paying fees to rebalance critical nodes. More likely than not, when they need to balance between each other they coordinate zero-fee swaps over private channels, submarine swaps, or something else.

Then you have the "experienced router" who will use fluctuating fees to rebalance channels, but the fees will usually be set low to attract volume. They may have their mSat/kSat rate set to 40, 50, 100 or 250 for a balanced channel, but higher if the channel has remote liquidity, and lower if it has local liquidity.  They may apply a step-wise exponential curve based on channel liquidity percentage, or they may use a linear function, or a three-step high-middle-low strategy. In the case of triangle-swaps they will generally agree to temporarily set their fees for the balance but it shouldn't always be necessary if they have a high traffic node.

The fourth category is the 'altruistic node', these will have their node set to zero fees for everyone all-the-time. These can be an on-ramp for newcomers, but I'm unclear how they can sustain liquidity balance.

How do I benefit?

The conclusion I've come to during this rabbit-hole escapade is that the big-boys are all well-connected, and they usually don't want channels open to small-fry 0.1BTC nodes (if only for the inconvenience). The opportunity on lightning for us sub-BTC nodes is to stack-sats by setting fees low thus encourage high-transaction rate and connect to nodes a class higher and a class lower and in the same class as yours.

If you don't want to do the heavy-lifting when searching for the right nodes to connect with, I just discovered this.  Take a look at the Suggested Peers section of the terminal.lightning.engineering page for your node. They will give you some options to maximize your traffic if you aren't having enough success with triangle swaps.

When exactly are fees charged?

Its my understanding that fees are only charged when funds are sent out of a channel. For example, if you open a direct payment channel (on-chain) to a vendor to pay them with lightning, your lightning node will set-aside the fee before the payment goes out. The direct-connection will not pay any fees to the vendor because they are receiving the payment. In this case, the fee will be saved back into your node and you can spend it at a later time. In-effect a direct payment channel will incur no lightning fees.

In the case of your node routing someone else's payment, you do not collect a fee on the channel where the payment comes in, but you do on its way out. In the case of you being the destination node for the payment, your node collects no fees as the terminal receiving node.

Future-proof the network

Finally, I must mention base-fees. The common wisdom is that you want your base-fee set to one Sat per transaction. Some play with setting this to the minimum-but-non-zero value of 1 mSat/xaction. After listening to What Bitcoin Did and interviews with lightning developers, it appears this setting of 1Sat/xaction was intended only as a placeholder and for demonstration, and upon further reflection it should have been zero in production. This makes sense, particularly for us small-fries that want to encourage transactions. It also makes the network more flexible for purposes like messaging (sphinx) and other pico-transaction (dust) based applications. So I have to pass this along; Please consider setting your base fees to zero.

What about flooding/DDOS attacks you say? There are a few solutions for this, one is setting the minimum HTLC (minimum transaction size) to something nonzero (1 Sat is default), or you may want to look into circuitbreaker. I'm giving circuitbreaker a chance with the suggested configuration, but I'm unclear if its working or not after only having run it for a few hours.

Sample Charge-LND Config

This is probably all you really want to see from this post.  I'm running this script with cron from the pi user (raspibolt) with the following command. The electrum-server isn't necessary with this config, but I left it in in case you have one and want to use the onchain_fee fee strategy.

I started out with the raspberry pi OS image and stepped through the raspibolt instructions. charge-lnd had a library problem that was resolved in a ticket somewhere on github for the project which is implemented by calling charge-lnd through a bash script that exports a special library environment variable. This bash script should be set to executable, and placed in the same directory as the charge-lnd binary (charge-lnd's default install path is ${HOME}/.local/bin/.)

  • charge-lnd.sh
#!/bin/bash

export LD_PRELOAD=/usr/lib/arm-linux-gnueabihf/libatomic.so.1.2.0
# Yes the following line is intended to be obscure and cute, but it works
`dirname $0`/`basename $0 .sh` $@

The update script executes every hour on the 45-minute mark
  • crontab line
45 * * * * /home/pi/.local/bin/charge-lnd.sh -c /home/pi/charge-lnd/charge.config --electrum-server bitcoin.lan:50002


 As a point of reference, below is what I have in my charge.conf for charge-lnd. It uses a linear fee strategy, directly proportional to the remote v.s. local liquidity ratio applied to a number between 1 and 100 mSat/kSat. If your channels are balanced exactly at 50/50 the outgoing fee will be about 50. It also automatically suspends fees for new and unbalanced channels so your triangle swaps can happen at zero fees without your intervention. I had to make a few changes to this config to get it to work as intended. You must add htlc-min/max and base_fee or any other channel setting that is different between classes to each class because if a channel transitions from one class to another the defaults won't be implied. This is probably a bug in charge-lnd, but is resolvable by keeping this in mind.

  • charge.conf
# With the settings below, when channels are balanced,
# the ppm fee will settle to 50 mSat/kSat
# This file is evaluated sequentially, and the first
# match will execute on a channel, then the next
# channel will be reevaluated from the top.
[default]
strategy = proportional

min_htlc_msat = 100
max_htlc_msat_ratio = 0.99
base_fee_msat = 0
min_fee_ppm = 0
max_fee_ppm = 100
min_fee_ppm_delta = 5
time_lock_delta = 40

# Friends are those you want to keep at zero
# fees indefinately, at least until you
# remove them from friends.list
[friends]
node.id = file:///home/pi/friends.list
strategy = static

max_htlc_msat_ratio = 0.99
base_fee_msat = 0
fee_ppm = 0

# keep fees at zero for new incoming channels
# they haven't yet balanced, and their age is
# less than 5 days (720 blocks)
# This should give your triangle swaps sufficient
# time to balance
[new_incoming_channels]
chan.max_ratio = 0.05
chan.max_age = 720
strategy = static

max_htlc_msat_ratio = 0.55
base_fee_msat = 0
fee_ppm = 0

# keep fees at zero for new outgoing channels
# they haven't yet balanced, and their age is
# less than 5 days (720 blocks)
# This should give your triangle swaps sufficient
# time to balance
[new_outgoing_channels]
chan.min_ratio = 0.95
chan.max_age = 720
strategy = static

max_htlc_msat_ratio = 0.55
base_fee_msat = 0
fee_ppm = 0

# Cap fee at 5ppm when you have 90% or more
# liquidity
[high_balances]
chan.min_ratio = 0.9
strategy = static

max_htlc_msat_ratio = 0.99
base_fee_msat = 0
fee_ppm = 5

# Cap fee at 2250ppm when you have 5% or less
# liquidity and limit HTLCs to 10 kSats
[scarce_balances]
chan.max_ratio = 0.05
strategy = static

base_fee_msat = 0
max_htlc_msat = 10000
fee_ppm = 2250

# Cap fee at 250ppm when a channel has 20% or less
# liquidity, and limit HTLCs to 105 kSats
[low_balances]
chan.max_ratio = 0.2
strategy = static

base_fee_msat = 0
max_htlc_msat = 105000
fee_ppm = 250

5 Comments

GIGA DORIRU BUREIKA

GIGA DORIRU BUREIKA wrote about 2 years ago

Thanks! This helps alot.


LN+

LN+ Admin wrote about 2 years ago

Thank you for writing this up! Sharing on Twitter.


Goldman_Sucks

Goldman_Sucks wrote over 1 year ago

I appreciate you for your code.

By the way, what does time_lock_delta = 40 mean?


epicurious

epicurious wrote over 1 year ago

Hi Goldman_Sucks, not sure if you found the answer to this, but for everyone's benefit, time_lock_delta = 40 enforces 40 blocks as the minimum number of blocks your node requires are added to the expiry of HTLCs. Here are some good explainers about what this means and optimal values: 

https://bitcoin.stackexchange.com/questions/89658/what-is-a-decent-value-for-time-lock-delta-on-a-lightning-network-node 

https://bitcoin.stackexchange.com/questions/87294/what-is-the-unit-of-time-lock-delta-in-lnd


Goldman_Sucks

Goldman_Sucks wrote over 1 year ago

Thanks.

Please login to post comments.

Lightning Network Node
refinery
Rank: 0
Capacity: 93,585 SAT
Channels: 1

Latest news