loop: add instantout cmd

pull/651/head
sputn1ck 5 months ago
parent 7cafbe957d
commit 8c7c7cf8b5
No known key found for this signature in database
GPG Key ID: 671103D881A5F0E4

@ -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{
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
}

@ -148,6 +148,7 @@ func main() {
listSwapsCommand, swapInfoCommand, getLiquidityParamsCommand,
setLiquidityRuleCommand, suggestSwapCommand, setParamsCommand,
getInfoCommand, abandonSwapCommand, reservationsCommands,
instantOutCommand,
}
err := app.Run(os.Args)

Loading…
Cancel
Save