Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split fees between liquidity ads and swap-in #709

Merged
merged 2 commits into from
Oct 9, 2024
Merged

Split fees between liquidity ads and swap-in #709

merged 2 commits into from
Oct 9, 2024

Conversation

pm47
Copy link
Member

@pm47 pm47 commented Oct 8, 2024

A channel open triggered by a swap-in is technically also a liquidity operation (with 1 sat liquidity required), thus causing two consecutive db events: a liquidity purchase followed by a swap-in. Both actions happen with the same on-chain transaction and we must be careful not to count mining fees twice.

@pm47 pm47 requested a review from t-bast October 8, 2024 16:51
Comment on lines 136 to 137
serviceFee = 0.msat,
miningFee = action.fundingTx.sharedTx.tx.localFees.truncateToSatoshi(),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note how we are not using the channelOrigin anymore. There is a certain amount of duplication regarding fee calculation here, I'm not sure how we can simplify. We could revisit later.

Comment on lines -877 to -888
// If we received or sent funds as part of the splice, we will add a corresponding entry to our incoming/outgoing payments db
addAll(origins.map { origin ->
ChannelAction.Storage.StoreIncomingPayment.ViaSpliceIn(
amountReceived = origin.amountReceived(),
serviceFee = origin.fees.serviceFee.toMilliSatoshi(),
miningFee = origin.fees.miningFee,
localInputs = action.fundingTx.sharedTx.tx.localInputs.map { it.outPoint }.toSet(),
txId = action.fundingTx.txId,
origin = origin
)
})
// If we added some funds ourselves it's a swap-in
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was two separate ViaSpliceIn sections, I'm not sure why.

if (action.fundingTx.sharedTx.tx.localInputs.isNotEmpty()) add(
ChannelAction.Storage.StoreIncomingPayment.ViaSpliceIn(
amountReceived = action.fundingTx.sharedTx.tx.localInputs.map { i -> i.txOut.amount }.sum().toMilliSatoshi() - action.fundingTx.sharedTx.tx.localFees,
serviceFee = 0.msat,
miningFee = action.fundingTx.sharedTx.tx.localFees.truncateToSatoshi(),
localInputs = action.fundingTx.sharedTx.tx.localInputs.map { it.outPoint }.toSet(),
txId = action.fundingTx.txId,
origin = null
origin = origins.filterIsInstance<Origin.OnChainWallet>().firstOrNull()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The model allows multiple origins but I don't think we can have several of the OnChainWallet type. We must have an origin otherwise the event will not be stored in db due to

.

Note that I have a full rework of the incoming payments db on another branch where this particular part is cleaned up.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was also wondering whether we should get rid of the multiple origins or not. If you're fixing this on another branch, that's great!

Comment on lines 122 to 129
// If we purchased liquidity as part of the channel creation, we will add it to our payments db
liquidityPurchase?.let { purchase ->
if (channelParams.localParams.isChannelOpener) {
// We only count fees corresponding to the liquidity purchase (i.e. their inputs).
add(ChannelAction.Storage.StoreOutgoingPayment.ViaInboundLiquidityRequest(txId = action.fundingTx.txId, purchase = purchase))
add(ChannelAction.EmitEvent(LiquidityEvents.Purchased(purchase)))
}
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We move this event up because it makes more sense to have it happen "before".

When we are purchasing liquidity without any other splice operation
(splice-in, splice-out or splice-cpfp), we will have a single entry
in our DB: the liquidity purchase itself. In that case, we are paying
mining fees for the shared input and output: we must record those in
the purchase. This happens during manual liquidity purchases and
on-the-fly funding.

We cannot easily add tests because we don't implement the liquidity
seller side, which makes it hard to trigger those conditions.
@pm47 pm47 merged commit e16e7f5 into master Oct 9, 2024
2 checks passed
@pm47 pm47 deleted the swap-fee-split branch October 9, 2024 11:13
dpad85 added a commit to ACINQ/phoenix that referenced this pull request Oct 9, 2024
With ACINQ/lightning-kmp#709 the mining
fee is split between local and purchase fees. However, for compat
with legacy leases, we do not store the local fee separately in
the database. Instead we store the full mining fee and calculate
the local fee by subtracting this full fee with the purchase fee.
dpad85 added a commit to ACINQ/phoenixd that referenced this pull request Oct 10, 2024
With ACINQ/lightning-kmp#709 and
ACINQ/lightning-kmp#710, the mining
fee for liquidity events is split between local and purchase
fee. However, we don't directly store the split fees, for
backward compatibility reasons. Thus, to get the local fee
from the database, we must compute it back.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants