From d3cbf592af6e26433905a8769dbcbf531491a82d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jos=C3=A9=20A=2EP?=
<53834183+Jossec101@users.noreply.github.com>
Date: Mon, 28 Oct 2024 12:25:17 +0100
Subject: [PATCH] Fix outputs for sweep transactions (#399)
* Added consts for sweep min tx amount in sats, conf target
* Fix creating dust utxos and 100K utxos, now we require a min amount to be swept out of the LND hot wallet, this shall ensure that there's more than a 100K utxo in the wallet
* bump to 25M sats minimum default sweep tx
---
src/Helpers/Constants.cs | 13 +++++++++++++
src/Jobs/SweepNodeWalletsJob.cs | 34 ++++++++++++++++-----------------
2 files changed, 30 insertions(+), 17 deletions(-)
diff --git a/src/Helpers/Constants.cs b/src/Helpers/Constants.cs
index 046ec18c..fdfa1592 100644
--- a/src/Helpers/Constants.cs
+++ b/src/Helpers/Constants.cs
@@ -75,6 +75,7 @@ public class Constants
public static readonly decimal MAXIMUM_WITHDRAWAL_BTC_AMOUNT = 21_000_000;
public static readonly int TRANSACTION_CONFIRMATION_MINIMUM_BLOCKS;
public static readonly long ANCHOR_CLOSINGS_MINIMUM_SATS;
+ public static readonly long MINIMUM_SWEEP_TRANSACTION_AMOUNT_SATS = 25_000_000; //25M sats
public static readonly string DEFAULT_DERIVATION_PATH = "48'/1'";
public static readonly int SESSION_TIMEOUT_MILLISECONDS = 3_600_000;
@@ -86,6 +87,11 @@ public class Constants
///
public static decimal MAX_TX_FEE_RATIO = 0.5m;
+ ///
+ /// The target number of confirmations blocks (fee rate) for the sweep transaction
+ ///
+ public static int SWEEP_CONF_TARGET = 6;
+
public const string IsFrozenTag = "frozen";
public const string IsManuallyFrozenTag = "manually_frozen";
@@ -217,6 +223,13 @@ static Constants()
var anchorClosingMinSats = GetEnvironmentalVariableOrThrowIfNotTesting("ANCHOR_CLOSINGS_MINIMUM_SATS");
if (anchorClosingMinSats != null) ANCHOR_CLOSINGS_MINIMUM_SATS = long.Parse(anchorClosingMinSats); // Check https://github.com/lightningnetwork/lnd/issues/6505#issuecomment-1120364460 to understand, we need 100K+ to support anchor channel closings
+ var sweepConfTarget = Environment.GetEnvironmentVariable("SWEEP_CONF_TARGET");
+ if (sweepConfTarget != null) SWEEP_CONF_TARGET = int.Parse(sweepConfTarget);
+
+ var minSweepTransactionAmount = Environment.GetEnvironmentVariable("MINIMUM_SWEEP_TRANSACTION_AMOUNT_SATS");
+ if (minSweepTransactionAmount != null) MINIMUM_SWEEP_TRANSACTION_AMOUNT_SATS = long.Parse(minSweepTransactionAmount);
+
+
DEFAULT_DERIVATION_PATH = GetEnvironmentalVariableOrThrowIfNotTesting("DEFAULT_DERIVATION_PATH") ?? DEFAULT_DERIVATION_PATH;
var timeout = Environment.GetEnvironmentVariable("SESSION_TIMEOUT_MILLISECONDS");
diff --git a/src/Jobs/SweepNodeWalletsJob.cs b/src/Jobs/SweepNodeWalletsJob.cs
index 19d5f818..ca5a72d9 100644
--- a/src/Jobs/SweepNodeWalletsJob.cs
+++ b/src/Jobs/SweepNodeWalletsJob.cs
@@ -44,7 +44,7 @@ public SweepNodeWalletsJob(ILogger logger,
INBXplorerService nbXplorerService,
ILightningClientService lightningClientService)
{
-
+
_logger = logger;
_nodeRepository = nodeRepository;
_walletRepository = walletRepository;
@@ -66,7 +66,7 @@ public async Task Execute(IJobExecutionContext context)
#region Local functions
async Task SweepFunds(Node node, Wallet wallet, Lightning.LightningClient lightningClient
- ,List utxos)
+ , List utxos)
{
if (node == null) throw new ArgumentNullException(nameof(node));
if (wallet == null) throw new ArgumentNullException(nameof(wallet));
@@ -81,9 +81,9 @@ async Task SweepFunds(Node node, Wallet wallet, Lightning.LightningClient lightn
if (node.ChannelAdminMacaroon != null)
{
var lndChangeAddress = await lightningClient.NewAddressAsync(new NewAddressRequest
- {
- Type = AddressType.UnusedWitnessPubkeyHash
- },
+ {
+ Type = AddressType.UnusedWitnessPubkeyHash
+ },
new Metadata
{
{
@@ -92,21 +92,21 @@ async Task SweepFunds(Node node, Wallet wallet, Lightning.LightningClient lightn
});
var totalSatsAvailable = utxos.Sum(x => x.AmountSat);
- if (returningAddress != null && lndChangeAddress != null && utxos.Any() && totalSatsAvailable > requiredAnchorChannelClosingAmount)
+ if (returningAddress != null && lndChangeAddress != null && utxos.Any() && totalSatsAvailable > Constants.MINIMUM_SWEEP_TRANSACTION_AMOUNT_SATS)
{
- var sweepedFundsAmount = (long)((totalSatsAvailable - requiredAnchorChannelClosingAmount) * 0.9); // We should let requiredAnchorChannelClosingAmount sats as a UTXO in in the hot wallet for channel closings
+ // We need to maintain onchain balance to be at least RequiredAnchorChannelClosingAmount but also we apply a 10% buffer to pay for this sweep fees and let some more money on the wallet
+ var sweepedFundsAmount = (long)((totalSatsAvailable - requiredAnchorChannelClosingAmount) * 0.9);
var sendManyResponse = await lightningClient.SendManyAsync(new SendManyRequest()
- {
- AddrToAmount =
+ {
+ AddrToAmount =
{
{returningAddress.Address.ToString(), sweepedFundsAmount}, //Sweeped funds
- {lndChangeAddress.Address, requiredAnchorChannelClosingAmount},
},
- MinConfs = 1,
- Label = $"Hot wallet Sweep tx on {DateTime.UtcNow.ToString("O")} to walletId:{wallet.Id}",
- SpendUnconfirmed = false,
- TargetConf = 1 // 1 for now TODO Maybe this can be set as a global env var for all the Target blocks of the FM..
- },
+ MinConfs = 6,
+ Label = $"Hot wallet Sweep tx on {DateTime.UtcNow.ToString("O")} to walletId:{wallet.Id}",
+ SpendUnconfirmed = false,
+ TargetConf = Constants.SWEEP_CONF_TARGET
+ },
new Metadata
{
{
@@ -157,9 +157,9 @@ async Task SweepFunds(Node node, Wallet wallet, Lightning.LightningClient lightn
try
{
-
+
var client = _lightningClientService.GetLightningClient(node.Endpoint);
-
+
var unspentResponse = await client.ListUnspentAsync(new ListUnspentRequest { MinConfs = 1, MaxConfs = Int32.MaxValue }, new Metadata
{
{