Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions loopd/swapclient_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -1966,6 +1966,10 @@ func (s *swapClientServer) ListStaticAddressSwaps(ctx context.Context,

var clientSwaps []*looprpc.StaticAddressLoopInSwap
for _, swp := range swaps {
if swp == nil {
continue
}

chainParams, err := s.network.ChainParams()
if err != nil {
return nil, fmt.Errorf("error getting chain params")
Expand Down Expand Up @@ -2005,13 +2009,19 @@ func (s *swapClientServer) ListStaticAddressSwaps(ctx context.Context,
if swp.SelectedAmount > 0 {
swapAmount = swp.SelectedAmount
}
costServer := staticAddressLoopInSwapServerCost(swp)
initiationTime := staticAddressLoopInTimestamp(swp.InitiationTime)
lastUpdateTime := staticAddressLoopInTimestamp(swp.LastUpdateTime)
swap := &looprpc.StaticAddressLoopInSwap{
SwapHash: swp.SwapHash[:],
DepositOutpoints: swp.DepositOutpoints,
State: state,
SwapAmountSatoshis: int64(swapAmount),
PaymentRequestAmountSatoshis: payReqAmount,
Deposits: protoDeposits,
InitiationTime: initiationTime,
LastUpdateTime: lastUpdateTime,
CostServer: costServer,
}

clientSwaps = append(clientSwaps, swap)
Expand All @@ -2022,6 +2032,31 @@ func (s *swapClientServer) ListStaticAddressSwaps(ctx context.Context,
}, nil
}

func staticAddressLoopInTimestamp(t time.Time) int64 {
if t.IsZero() {
return 0
}

return t.UnixNano()
}

// staticAddressLoopInSwapServerCost returns the paid server cost using the
// legacy ListSwaps cost semantics. Static loop-ins currently only persist the
// accepted quote fee, and that fee is paid once the swap invoice settles.
// Timeout-path miner fees are not persisted, so cost_onchain and cost_offchain
// remain zero instead of returning an estimate as an actual cost.
func staticAddressLoopInSwapServerCost(swp *loopin.StaticAddressLoopIn) int64 {
switch swp.GetState() {
case loopin.PaymentReceived, loopin.Succeeded,
loopin.SucceededTransitioningFailed:

return int64(swp.QuotedSwapFee)

default:
return 0
}
}

// GetStaticAddressSummary returns a summary of static address-related
// information. Amongst deposits and withdrawals and their total values, it also
// includes a list of detailed deposit information filtered by their state.
Expand Down
67 changes: 67 additions & 0 deletions loopd/swapclient_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/lightninglabs/loop/looprpc"
"github.com/lightninglabs/loop/staticaddr/address"
"github.com/lightninglabs/loop/staticaddr/deposit"
"github.com/lightninglabs/loop/staticaddr/loopin"
"github.com/lightninglabs/loop/staticaddr/script"
"github.com/lightninglabs/loop/swap"
mock_lnd "github.com/lightninglabs/loop/test"
Expand Down Expand Up @@ -306,6 +307,72 @@ func TestSetLiquidityParamsRejectsStaticAutoloopWithoutExperimental(
require.ErrorContains(t, err, "--experimental")
}

// TestStaticAddressLoopInTimestamp verifies that zero timestamps are omitted
// from static loop-in responses instead of passing a zero time to UnixNano.
func TestStaticAddressLoopInTimestamp(t *testing.T) {
require.Zero(t, staticAddressLoopInTimestamp(time.Time{}))

timestamp := time.Unix(1_234, 567).UTC()
require.Equal(
t, timestamp.UnixNano(),
staticAddressLoopInTimestamp(timestamp),
)
}

// TestStaticAddressLoopInSwapServerCost verifies that static loop-in server
// costs are only reported once the invoice payment was received. Timeout path
// costs are not persisted today, so they are intentionally not estimated here.
func TestStaticAddressLoopInSwapServerCost(t *testing.T) {
const quoteFee = btcutil.Amount(1_234)

tests := []struct {
name string
state fsm.StateType
wantServer int64
}{
{
name: "pending before payment",
state: loopin.SignHtlcTx,
},
{
name: "payment received",
state: loopin.PaymentReceived,
wantServer: int64(quoteFee),
},
{
name: "succeeded",
state: loopin.Succeeded,
wantServer: int64(quoteFee),
},
{
name: "succeeded transition failed",
state: loopin.SucceededTransitioningFailed,
wantServer: int64(quoteFee),
},
{
name: "timeout swept",
state: loopin.HtlcTimeoutSwept,
},
{
name: "failed",
state: loopin.Failed,
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
swap := &loopin.StaticAddressLoopIn{
QuotedSwapFee: quoteFee,
}
swap.SetState(test.state)

costServer := staticAddressLoopInSwapServerCost(swap)

require.Equal(t, test.wantServer, costServer)
})
}
}

// TestRPCAutoloopReasonStaticLoopInNoCandidate verifies that the new planner
// reason is exposed over rpc.
func TestRPCAutoloopReasonStaticLoopInNoCandidate(t *testing.T) {
Expand Down
58 changes: 55 additions & 3 deletions looprpc/client.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions looprpc/client.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2143,6 +2143,31 @@ message StaticAddressLoopInSwap {
The deposits that were used for this swap.
*/
repeated Deposit deposits = 6;

/*
Initiation time of the swap.
*/
int64 initiation_time = 7;

/*
Last update time of the swap.
*/
int64 last_update_time = 8;

/*
Swap server cost.
*/
int64 cost_server = 9;

/*
On-chain transaction cost.
*/
int64 cost_onchain = 10;

/*
Off-chain routing fees.
*/
int64 cost_offchain = 11;
}

enum StaticAddressLoopInSwapState {
Expand Down
25 changes: 25 additions & 0 deletions looprpc/client.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -2807,6 +2807,31 @@
"$ref": "#/definitions/looprpcDeposit"
},
"description": "The deposits that were used for this swap."
},
"initiation_time": {
"type": "string",
"format": "int64",
"description": "Initiation time of the swap."
},
"last_update_time": {
"type": "string",
"format": "int64",
"description": "Last update time of the swap."
},
"cost_server": {
"type": "string",
"format": "int64",
"description": "Swap server cost."
},
"cost_onchain": {
"type": "string",
"format": "int64",
"description": "On-chain transaction cost."
},
"cost_offchain": {
"type": "string",
"format": "int64",
"description": "Off-chain routing fees."
}
}
},
Expand Down
Loading