Skip to content
Merged
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
165 changes: 165 additions & 0 deletions cmd/loop/instantout.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
package main

import (
"context"
"fmt"
"strconv"
"strings"

"github.com/lightninglabs/loop/instantout/reservation"
"github.com/lightninglabs/loop/looprpc"
"github.com/urfave/cli"
)

var instantOutCommand = cli.Command{
Copy link
Member

Choose a reason for hiding this comment

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

Would be cool to have a cli command to also list available reservations. (can be a follow up).

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 have that loop r l

Copy link
Member Author

Choose a reason for hiding this comment

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

Or do you mean list instantouts? (we should have that as well)

Name: "instantout",
Usage: "perform an instant off-chain to on-chain swap (looping out)",
Description: `
Attempts to instantly loop out into the backing lnd's wallet. The amount
will be chosen via the cli.
`,
Flags: []cli.Flag{
cli.StringFlag{
Name: "channel",
Usage: "the comma-separated list of short " +
"channel IDs of the channels to loop out",
},
},
Action: instantOut,
}

func instantOut(ctx *cli.Context) error {
// Parse outgoing channel set. Don't string split if the flag is empty.
// Otherwise, strings.Split returns a slice of length one with an empty
// element.
var outgoingChanSet []uint64
if ctx.IsSet("channel") {
chanStrings := strings.Split(ctx.String("channel"), ",")
for _, chanString := range chanStrings {
chanID, err := strconv.ParseUint(chanString, 10, 64)
if err != nil {
return fmt.Errorf("error parsing channel id "+
"\"%v\"", chanString)
}
outgoingChanSet = append(outgoingChanSet, chanID)
}
}

// First set up the swap client itself.
client, cleanup, err := getClient(ctx)
if err != nil {
return err
}
defer cleanup()

// Now we fetch all the confirmed reservations.
reservations, err := client.ListReservations(
context.Background(), &looprpc.ListReservationsRequest{},
)
if err != nil {
return err
}

var (
confirmedReservations []*looprpc.ClientReservation
totalAmt int64
idx int
)

for _, res := range reservations.Reservations {
if res.State != string(reservation.Confirmed) {
continue
}

confirmedReservations = append(confirmedReservations, res)
}

if len(confirmedReservations) == 0 {
fmt.Printf("No confirmed reservations found \n")
return nil
}

fmt.Printf("Available reservations: \n\n")
for _, res := range confirmedReservations {
idx++
fmt.Printf("Reservation %v: %v \n", idx, res.Amount)
totalAmt += int64(res.Amount)
}

fmt.Println()
fmt.Printf("Max amount to instant out: %v\n", totalAmt)
fmt.Println()

fmt.Println("Select reservations for instantout (e.g. '1,2,3')")
fmt.Println("Type 'ALL' to use all available reservations.")

var answer string
fmt.Scanln(&answer)

// Parse
var selectedReservations [][]byte
switch answer {
case "ALL":
for _, res := range confirmedReservations {
selectedReservations = append(
selectedReservations,
res.ReservationId,
)
}

case "":
return fmt.Errorf("no reservations selected")

default:
selectedIndexes := strings.Split(answer, ",")
selectedIndexMap := make(map[int]struct{})
for _, idxStr := range selectedIndexes {
idx, err := strconv.Atoi(idxStr)
if err != nil {
return err
}
if idx < 0 {
return fmt.Errorf("invalid index %v", idx)
}

if idx > len(confirmedReservations) {
return fmt.Errorf("invalid index %v", idx)
}
if _, ok := selectedIndexMap[idx]; ok {
return fmt.Errorf("duplicate index %v", idx)
}

selectedReservations = append(
selectedReservations,
confirmedReservations[idx-1].ReservationId,
)

selectedIndexMap[idx] = struct{}{}
}
}

fmt.Println("Starting instant swap out")

// Now we can request the instant out swap.
instantOutRes, err := client.InstantOut(
context.Background(),
&looprpc.InstantOutRequest{
ReservationIds: selectedReservations,
OutgoingChanSet: outgoingChanSet,
},
)

if err != nil {
return err
}

fmt.Printf("Instant out swap initiated with ID: %x, State: %v \n",
instantOutRes.InstantOutHash, instantOutRes.State)

if instantOutRes.SweepTxId != "" {
fmt.Printf("Sweepless sweep tx id: %v \n",
instantOutRes.SweepTxId)
}

return nil
}
1 change: 1 addition & 0 deletions cmd/loop/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ func main() {
listSwapsCommand, swapInfoCommand, getLiquidityParamsCommand,
setLiquidityRuleCommand, suggestSwapCommand, setParamsCommand,
getInfoCommand, abandonSwapCommand, reservationsCommands,
instantOutCommand,
}

err := app.Run(os.Args)
Expand Down
4 changes: 2 additions & 2 deletions fsm/example_fsm.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
stateDiagram-v2
[*] --> InitFSM: OnRequestStuff
InitFSM
InitFSM --> StuffFailed: OnError
InitFSM --> StuffSentOut: OnStuffSentOut
InitFSM --> StuffFailed: OnError
StuffFailed
StuffSentOut
StuffSentOut --> StuffFailed: OnError
StuffSentOut --> StuffSuccess: OnStuffSuccess
StuffSentOut --> StuffFailed: OnError
StuffSuccess
```
8 changes: 8 additions & 0 deletions fsm/stateparser/stateparser.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"sort"

"github.com/lightninglabs/loop/fsm"
"github.com/lightninglabs/loop/instantout"
"github.com/lightninglabs/loop/instantout/reservation"
)

Expand Down Expand Up @@ -49,6 +50,13 @@ func run() error {
return err
}

case "instantout":
instantout := &instantout.FSM{}
err = writeMermaidFile(fp, instantout.GetV1ReservationStates())
if err != nil {
return err
}

default:
fmt.Println("Missing or wrong argument: fsm must be one of:")
fmt.Println("\treservations")
Expand Down
Loading