A core lightning plugin to automatically rebalance multiple channels.
For general plugin installation instructions see the plugins repo README.md
Release binaries for
can be found on the release page. If you are unsure about your architecture you can run uname -m
.
They require glibc>=2.31
, which you can check with ldd --version
.
You can build the plugin yourself instead of using the release binaries. First clone the repo:
git clone https://github.com/daywalker90/sling.git
Install a recent rust version (rustup is recommended) and cd
into the sling
folder, then:
cargo build --release
After that the binary will be here: target/release/sling
Note: Release binaries are built using cross
and the optimized
profile.
sling-version
print the version of the pluginsling-job
adds a rebalancing job for a channel, you can only have one job per channel and if you add one for the same channel it gets stopped and updated inplacesling-jobsettings
provide a ShortChannelId (or nothing for all channels) to list the currently saved settings for the job(s)sling-go
start all jobs that are not already running, or the job specified by a ShortChannelIdsling-stop
gracefully stop all running jobs or the job specified by a ShortChannelId, jobs take up to sling-timeoutpay
to actually stopsling-stats
with no arguments this shows a status overview for all jobs, you can use an optional boolean argument to set the output to json: sling-stats true
. Otherwise you can provide a ShortChannelId to get more detailed stats for that specific job, which is always in json format.sling-deletejob
gracefully stops and removes all jobs by providing the keyword all
or a single job by providing a ShortChannelId. Does not remove raw stats from disk.sling-except-chan
add or remove ShortChannelIds to completely avoid or alternatively list all current exceptions with keyword list
.sling-except-peer
same as sling-except-chan
but with node PublicKeysTo pull sats into a channel you can add a job like this:
sling-job -k scid direction amount maxppm (outppm) (target) (maxhops) (candidates) (depleteuptopercent) (depleteuptoamount) (paralleljobs)
You can completely leave out optional (those in ()
) arguments, with one exception: either outppm and/or candidates must be set
:warning:You must use the -k keyword=value
format for sling-job
!
scid
: the ShortChannelId to which the sats should be pulled e.g. 704776x2087x3
direction
: set this to pull
to pull the sats into the channel declared by scid
amount
: the amount in sats used per rebalance operationmaxppm
: the max effective ppm to use for the rebalancesoutppm
: while building the list of channels to pull from, choose only the ones where we effectively charge <= outppm
target
: floating point between 0
and 1
. E.g.: if atleast 0.7
* channel_capacity is on our side, the job stops rebalancing and goes into idle. Default is 0.5
maxhops
: maximum number of hops allowed in a route. A hop is a node that is not us. Default is 8
candidates
: a list of our scid's to use for rebalancing this channel. E.g.: '["704776x2087x5","702776x1087x2"]'
You can still combine this with outppm
depleteuptopercent
: how much % to leave the candidates with on the local side of the channel as a floating point between 0 and <1. Default is 0.2
. Also see Depleteformula. You can set this globally, see Options.depleteuptoamount
: how many sats to leave the candidates with on the local side of the channel. Default is 2000000
sats. Also see Depleteformula. You can set this globally, see Options.paralleljobs
: How many routes to take in parallel for this job. Default is 1
. You can set this globally, see Options.Easy example: "Pull sats to our side on 704776x2087x3
in amounts of 100000 sats while paying max 300ppm and only using candidates where we charge 0ppm, use defaults (see Options) for the rest of the parameters":
sling-job -k scid=704776x2087x3 direction=pull amount=100000 maxppm=300 outppm=0
Advanced example: "Pull sats to our side on 704776x2087x3
in amounts of 100000 sats while paying max 300ppm. Idle when 0.8*capacity_of_704776x2087x3 is on our side, use max 6 hops and only these other channels i have as partners: 704776x2087x5
, 702776x1087x2
. Use defaults for the rest of the parameters and (because we omitted outppm
) ignore the ppm i charge on my candidates":
sling-job -k scid=704776x2087x3 direction=pull amount=100000 maxppm=300 target=0.8 maxhops=6 candidates='["704776x2087x5","702776x1087x2"]'
To push sats out of a channel you can add a job like this:
sling-job -k scid direction amount maxppm (outppm) (target) (maxhops) (candidates) (depleteuptopercent) (depleteuptoamount) (paralleljobs)
You can completely leave out optional (those in ()
) arguments, with one exception: either outppm and/or candidates must be set
:warning:You must use the -k keyword=value
format for sling-job
!
scid
: the ShortChannelId to push sats out of e.g. 704776x2087x3
direction
: set this to push
to make it clear to push the sats out of the channel declared by scid
amount
: the amount in sats used per rebalance operationmaxppm
: the max effective ppm to use for the rebalancesoutppm
: while building the list of channels to push into, choose only the ones where we effectively charge >= outppm
target
: floating point between 0
and 1
. E.g.: if atleast 0.7
* channel_capacity is on their side, the job stops rebalancing and goes into idle. Default is 0.5
maxhops
: maximum number of hops allowed in a route. A hop is a node that is not us. Default is 8
candidates
: a list of our scid's to use for rebalancing this channel. E.g.: '["704776x2087x5","702776x1087x2"]'
You can still combine this with outppm
depleteuptopercent
: how much % to leave the candidates with on the remote side of the channel as a floating point between 0 and <1. Default is 0.2
. Also see Depleteformula. You can set this globally, see Options.depleteuptoamount
: how many sats to leave the candidates with on the remote side of the channel. Default is 2000000
sats. Also see Depleteformula. You can set this globally, see Options.paralleljobs
: How many routes to take in parallel for this job. Default is 1
. You can set this globally, see Options.Easy example: "Push sats to their side on 704776x2087x3
in amounts of 100000 sats while paying max 300ppm and only using candidates where we charge >=600ppm, use defaults (see Options) for the rest of the parameters":
sling-job -k scid=704776x2087x3 direction=push amount=100000 maxppm=300 outppm=600
Advanced example: "Push sats to their side on 704776x2087x3
in amounts of 100000 sats while paying max 300ppm. Idle when 0.8*capacity_of_704776x2087x3 is on their side, use max 6 hops and only these other channels i have as partners: 704776x2087x5
, 702776x1087x2
. Use defaults for the rest of the parameters and (because we omitted outppm
) ignore the ppm i charge on my candidates":
sling-job -k scid=704776x2087x3 direction=push amount=100000 maxppm=300 target=0.8 maxhops=6 candidates='["704776x2087x5","702776x1087x2"]'
Formula is min(depleteuptopercent * channel_capacity, depleteuptoamount)
. If you don't set one or both, the global default will be used for one or both respectively instead. You can change the global defaults here: Options
sling
is a dynamic plugin with dynamic options, so you can start it after CLN is already running and modify it's options after the plugin is started. You have two different methods of setting the options:
lightning-cli -k plugin subcommand=start plugin=/path/to/sling sling-refresh-peers-interval=6
setconfig
command) make sure the plugin will start automatically with CLN (include plugin=/path/to/sling
or have a symlink to sling
in your plugins
folder). This is because CLN will refuse to start with config options that don't have a corresponding plugin loaded. :warning:If you edit your config file manually while CLN is running and a line changes their line number CLN will crash when you use the setconfig command, so better stick to setconfig
only during CLN's uptime!lightning-cli setconfig sling-refresh-peers-interval 6
You can mix two methods and if you set the same option with different methods, it will pick the value from your most recently used method.
sling-refresh-peers-interval
: sling
periodically calls listpeers every refresh-peers-interval
seconds
and jobs use the data of the last call to check for balances etc. So this option could severely impact rebalancing target precision
if it's value is too high. Default is 1
ssling-refresh-aliasmap-interval
: How often to refresh node aliases in seconds. Default is every 3600
ssling-refresh-gossmap-interval
: How often to read gossip_store
updates in seconds. Default is every 10
ssling-reset-liquidity-interval
: After how many minutes to reset liquidity knowledge. Default is 360
msling-depleteuptopercent
: Up to what percent to pull/push sats from/to candidate channels as floating point between 0 and <1. Also see Depleteformula. Default is 0.2
sling-depleteuptoamount
: Up to what amount to pull/push sats from/to candidate channels. Also see Depleteformula. Default is 2000000
satssling-maxhops
: Maximum number of hops allowed in a route. A hop is a node that is not us. Default is 8
sling-candidates-min-age
: Minimum age of channels to rebalance with in blocks. Default is 0
sling-paralleljobs
: How many routes to take in parallel for any job. Default is 1
sling-timeoutpay
: How long we wait for a rebalance to resolve. After this we just continue with the next route. Default is 120
ssling-max-htlc-count
: Max number of pending htlcs allowed in participating channels (softcap). Should be higher than your highest parraleljobs
. Default is 5
sling-stats-delete-failures-age
: Max age of failure stats in days and also time window for sling-stats. Default is 30
days, use 0
to never delete stats based on agesling-stats-delete-successes-age
: Max age of success stats in days and also time window for sling-stats. Default is 30
days, use 0
to never delete stats based on agesling-stats-delete-failures-size
: Max number of failure stats per channel. Default is 10000
, use 0
to never delete stats based on countsling-stats-delete-successes-size
: Max number of successes stats per channel. Default is 10000
, use 0
to never delete stats based on countYou can report issues, feedback etc. here on github or join this telegram channel: Telegram
Thank you to cdecker for helping me get into writing a plugin with cln-plugin, the people in https://t.me/lightningd and giovannizotta of the original circular plugin.