Problem
Static Loop-In responses expose cost_onchain, but the value is currently zero for every swap.
The current implementation only derives cost_server from the accepted quote after the invoice is paid. It does not populate an on-chain cost in either:
ListStaticAddressSwaps
- the generic
ListSwaps / monitor representation
This is also reflected in TestListStaticAddressSwapsPopulatesTimingAndCosts, which currently asserts that CostOnchain is zero.
For cost accounting equivalent to a legacy Loop-In, the fee paid by the transaction that funds the static-address deposit is the client-side on-chain cost. It is analogous to the legacy Loop-In HTLC funding transaction fee, even though the static-address funding transaction happens before the swap is initiated.
Expected behavior
When a static Loop-In consumes one or more deposits:
- report the funding transaction fee as
cost_onchain;
- aggregate the fees for all selected deposits;
- deduplicate by funding transaction ID, so two selected outputs from the same transaction do not count the full transaction fee twice;
- populate the value consistently in
ListStaticAddressSwaps, generic ListSwaps, and monitor updates;
- persist the actual cost so terminal autoloop budget accounting can use it instead of estimates.
For deposits funded by an external wallet, Loop may not know the sender's fee. Those should be treated explicitly as unavailable/client-external rather than being confused with a known zero-fee transaction.
Straightforward implementation
The existing lnd client already provides the required data:
txs, err := lndClient.ListTransactions(ctx, startHeight, -1)
Each lndclient.Transaction includes TxHash and Fee.
An initial implementation can:
- Parse the selected deposit outpoints and collect their unique funding transaction hashes.
- Fetch wallet transactions once and build a
txid -> fee map.
- Sum each unique funding transaction fee once.
- Store the result in the existing base
swaps.onchain_cost column. Static Loop-Ins already have a row in swaps, so a new cost column should not be necessary.
- Add the persisted cost to
StaticAddressLoopIn when reconstructing it from the joined SQL row.
- Use that cost in both static-specific and generic RPC representations.
The common loop static new --sendcoins flow normally creates one static deposit output, so attributing the full wallet transaction fee is straightforward. If a single transaction creates multiple static deposits that can be consumed by different swaps, the implementation should use a deterministic per-deposit allocation or a transaction-level accounting record so the fee is not charged in full to multiple swaps.
The cost should be attached when the deposits are definitively consumed (successful Loop-In or a confirmed HTLC timeout path). A failed attempt that unlocks and reuses the same deposits should not cause the original funding fee to be counted again on a later swap.
Acceptance criteria
- A single-deposit static Loop-In reports its known wallet funding fee in
cost_onchain.
- A swap using deposits from different funding transactions reports the sum of their fees.
- Multiple selected deposits from the same funding transaction count that transaction fee only once.
- Failed attempts that release reusable deposits do not cause duplicate accounting.
- Static-specific and generic swap APIs return the same persisted on-chain cost.
- Tests cover single-deposit, multiple-transaction, same-transaction/multiple-output, unknown external funding, and restart behavior.
Problem
Static Loop-In responses expose
cost_onchain, but the value is currently zero for every swap.The current implementation only derives
cost_serverfrom the accepted quote after the invoice is paid. It does not populate an on-chain cost in either:ListStaticAddressSwapsListSwaps/ monitor representationThis is also reflected in
TestListStaticAddressSwapsPopulatesTimingAndCosts, which currently asserts thatCostOnchainis zero.For cost accounting equivalent to a legacy Loop-In, the fee paid by the transaction that funds the static-address deposit is the client-side on-chain cost. It is analogous to the legacy Loop-In HTLC funding transaction fee, even though the static-address funding transaction happens before the swap is initiated.
Expected behavior
When a static Loop-In consumes one or more deposits:
cost_onchain;ListStaticAddressSwaps, genericListSwaps, and monitor updates;For deposits funded by an external wallet, Loop may not know the sender's fee. Those should be treated explicitly as unavailable/client-external rather than being confused with a known zero-fee transaction.
Straightforward implementation
The existing lnd client already provides the required data:
Each
lndclient.TransactionincludesTxHashandFee.An initial implementation can:
txid -> feemap.swaps.onchain_costcolumn. Static Loop-Ins already have a row inswaps, so a new cost column should not be necessary.StaticAddressLoopInwhen reconstructing it from the joined SQL row.The common
loop static new --sendcoinsflow normally creates one static deposit output, so attributing the full wallet transaction fee is straightforward. If a single transaction creates multiple static deposits that can be consumed by different swaps, the implementation should use a deterministic per-deposit allocation or a transaction-level accounting record so the fee is not charged in full to multiple swaps.The cost should be attached when the deposits are definitively consumed (successful Loop-In or a confirmed HTLC timeout path). A failed attempt that unlocks and reuses the same deposits should not cause the original funding fee to be counted again on a later swap.
Acceptance criteria
cost_onchain.