cmd/loop: urfave/cli -> urfave/cli/v3

This commit is contained in:
Boris Nagaev 2025-10-06 15:17:26 -03:00
parent 0ff91716d9
commit 5e6e789496
No known key found for this signature in database
17 changed files with 541 additions and 544 deletions

View file

@ -7,7 +7,7 @@ import (
"context"
"github.com/lightninglabs/loop/looprpc"
"github.com/urfave/cli"
"github.com/urfave/cli/v3"
)
func init() {
@ -15,7 +15,7 @@ func init() {
commands = append(commands, forceAutoloopCmd)
}
var forceAutoloopCmd = cli.Command{
var forceAutoloopCmd = &cli.Command{
Name: "forceautoloop",
Usage: `
Forces to trigger an autoloop step, regardless of the current internal
@ -25,16 +25,14 @@ var forceAutoloopCmd = cli.Command{
Hidden: true,
}
func forceAutoloop(ctx *cli.Context) error {
client, cleanup, err := getDebugClient(ctx)
func forceAutoloop(ctx context.Context, cmd *cli.Command) error {
client, cleanup, err := getDebugClient(ctx, cmd)
if err != nil {
return err
}
defer cleanup()
cfg, err := client.ForceAutoLoop(
context.Background(), &looprpc.ForceAutoLoopRequest{},
)
cfg, err := client.ForceAutoLoop(ctx, &looprpc.ForceAutoLoopRequest{})
if err != nil {
return err
}
@ -44,13 +42,13 @@ func forceAutoloop(ctx *cli.Context) error {
return nil
}
func getDebugClient(ctx *cli.Context) (looprpc.DebugClient, func(), error) {
rpcServer := ctx.GlobalString("rpcserver")
tlsCertPath, macaroonPath, err := extractPathArgs(ctx)
func getDebugClient(ctx context.Context, cmd *cli.Command) (looprpc.DebugClient, func(), error) {
rpcServer := cmd.String("rpcserver")
tlsCertPath, macaroonPath, err := extractPathArgs(cmd)
if err != nil {
return nil, nil, err
}
conn, err := getClientConn(rpcServer, tlsCertPath, macaroonPath)
conn, err := getClientConn(ctx, rpcServer, tlsCertPath, macaroonPath)
if err != nil {
return nil, nil, err
}

View file

@ -4,10 +4,10 @@ import (
"context"
"github.com/lightninglabs/loop/looprpc"
"github.com/urfave/cli"
"github.com/urfave/cli/v3"
)
var getInfoCommand = cli.Command{
var getInfoCommand = &cli.Command{
Name: "getinfo",
Usage: "show general information about the loop daemon",
Description: "Displays general information about the daemon like " +
@ -16,16 +16,14 @@ var getInfoCommand = cli.Command{
Action: getInfo,
}
func getInfo(ctx *cli.Context) error {
client, cleanup, err := getClient(ctx)
func getInfo(ctx context.Context, cmd *cli.Command) error {
client, cleanup, err := getClient(ctx, cmd)
if err != nil {
return err
}
defer cleanup()
cfg, err := client.GetInfo(
context.Background(), &looprpc.GetInfoRequest{},
)
cfg, err := client.GetInfo(ctx, &looprpc.GetInfoRequest{})
if err != nil {
return err
}

View file

@ -9,10 +9,10 @@ import (
"github.com/lightninglabs/loop/instantout/reservation"
"github.com/lightninglabs/loop/looprpc"
"github.com/urfave/cli"
"github.com/urfave/cli/v3"
)
var instantOutCommand = cli.Command{
var instantOutCommand = &cli.Command{
Name: "instantout",
Usage: "perform an instant off-chain to on-chain swap (looping out)",
Description: `
@ -20,12 +20,12 @@ var instantOutCommand = cli.Command{
will be chosen via the cli.
`,
Flags: []cli.Flag{
cli.StringFlag{
&cli.StringFlag{
Name: "channel",
Usage: "the comma-separated list of short " +
"channel IDs of the channels to loop out",
},
cli.StringFlag{
&cli.StringFlag{
Name: "addr",
Usage: "the optional address that the looped out funds " +
"should be sent to, if let blank the funds " +
@ -35,13 +35,13 @@ var instantOutCommand = cli.Command{
Action: instantOut,
}
func instantOut(ctx *cli.Context) error {
func instantOut(ctx context.Context, cmd *cli.Command) 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"), ",")
if cmd.IsSet("channel") {
chanStrings := strings.Split(cmd.String("channel"), ",")
for _, chanString := range chanStrings {
chanID, err := strconv.ParseUint(chanString, 10, 64)
if err != nil {
@ -53,7 +53,7 @@ func instantOut(ctx *cli.Context) error {
}
// First set up the swap client itself.
client, cleanup, err := getClient(ctx)
client, cleanup, err := getClient(ctx, cmd)
if err != nil {
return err
}
@ -61,7 +61,7 @@ func instantOut(ctx *cli.Context) error {
// Now we fetch all the confirmed reservations.
reservations, err := client.ListReservations(
context.Background(), &looprpc.ListReservationsRequest{},
ctx, &looprpc.ListReservationsRequest{},
)
if err != nil {
return err
@ -156,7 +156,7 @@ func instantOut(ctx *cli.Context) error {
// Now that we have the selected reservations we can estimate the
// fee-rates.
quote, err := client.InstantOutQuote(
context.Background(), &looprpc.InstantOutQuoteRequest{
ctx, &looprpc.InstantOutQuoteRequest{
Amt: selectedAmt,
ReservationIds: selectedReservations,
},
@ -180,11 +180,11 @@ func instantOut(ctx *cli.Context) error {
// Now we can request the instant out swap.
instantOutRes, err := client.InstantOut(
context.Background(),
ctx,
&looprpc.InstantOutRequest{
ReservationIds: selectedReservations,
OutgoingChanSet: outgoingChanSet,
DestAddr: ctx.String("addr"),
DestAddr: cmd.String("addr"),
},
)
if err != nil {
@ -202,7 +202,7 @@ func instantOut(ctx *cli.Context) error {
return nil
}
var listInstantOutsCommand = cli.Command{
var listInstantOutsCommand = &cli.Command{
Name: "listinstantouts",
Usage: "list all instant out swaps",
Description: `
@ -211,16 +211,16 @@ var listInstantOutsCommand = cli.Command{
Action: listInstantOuts,
}
func listInstantOuts(ctx *cli.Context) error {
func listInstantOuts(ctx context.Context, cmd *cli.Command) error {
// First set up the swap client itself.
client, cleanup, err := getClient(ctx)
client, cleanup, err := getClient(ctx, cmd)
if err != nil {
return err
}
defer cleanup()
resp, err := client.ListInstantOuts(
context.Background(), &looprpc.ListInstantOutsRequest{},
ctx, &looprpc.ListInstantOutsRequest{},
)
if err != nil {
return err

View file

@ -7,7 +7,7 @@ import (
"time"
"github.com/lightninglabs/loop/looprpc"
"github.com/urfave/cli"
"github.com/urfave/cli/v3"
"gopkg.in/macaroon.v2"
)
@ -24,22 +24,22 @@ type printableToken struct {
FileName string `json:"file_name"`
}
var listAuthCommand = cli.Command{
var listAuthCommand = &cli.Command{
Name: "listauth",
Usage: "list all L402 tokens",
Description: "Shows a list of all L402 tokens that loopd has paid for",
Action: listAuth,
}
func listAuth(ctx *cli.Context) error {
client, cleanup, err := getClient(ctx)
func listAuth(ctx context.Context, cmd *cli.Command) error {
client, cleanup, err := getClient(ctx, cmd)
if err != nil {
return err
}
defer cleanup()
resp, err := client.GetL402Tokens(
context.Background(), &looprpc.TokensRequest{},
ctx, &looprpc.TokensRequest{},
)
if err != nil {
return err
@ -74,7 +74,7 @@ func listAuth(ctx *cli.Context) error {
return nil
}
var fetchL402Command = cli.Command{
var fetchL402Command = &cli.Command{
Name: "fetchl402",
Usage: "fetches a new L402 authentication token from the server",
Description: "Fetches a new L402 authentication token from the server. " +
@ -84,15 +84,15 @@ var fetchL402Command = cli.Command{
Action: fetchL402,
}
func fetchL402(ctx *cli.Context) error {
client, cleanup, err := getClient(ctx)
func fetchL402(ctx context.Context, cmd *cli.Command) error {
client, cleanup, err := getClient(ctx, cmd)
if err != nil {
return err
}
defer cleanup()
res, err := client.FetchL402Token(
context.Background(), &looprpc.FetchL402TokenRequest{},
ctx, &looprpc.FetchL402TokenRequest{},
)
if err != nil {
return err

View file

@ -10,12 +10,12 @@ import (
"github.com/lightninglabs/loop/liquidity"
"github.com/lightninglabs/loop/looprpc"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/urfave/cli"
"github.com/urfave/cli/v3"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
var getLiquidityParamsCommand = cli.Command{
var getLiquidityParamsCommand = &cli.Command{
Name: "getparams",
Usage: "show liquidity manager parameters",
Description: "Displays the current set of parameters that are set " +
@ -23,15 +23,15 @@ var getLiquidityParamsCommand = cli.Command{
Action: getParams,
}
func getParams(ctx *cli.Context) error {
client, cleanup, err := getClient(ctx)
func getParams(ctx context.Context, cmd *cli.Command) error {
client, cleanup, err := getClient(ctx, cmd)
if err != nil {
return err
}
defer cleanup()
cfg, err := client.GetLiquidityParams(
context.Background(), &looprpc.GetLiquidityParamsRequest{},
ctx, &looprpc.GetLiquidityParamsRequest{},
)
if err != nil {
return err
@ -42,13 +42,13 @@ func getParams(ctx *cli.Context) error {
return nil
}
var setLiquidityRuleCommand = cli.Command{
var setLiquidityRuleCommand = &cli.Command{
Name: "setrule",
Usage: "set liquidity manager rule for a channel/peer",
Description: "Update or remove the liquidity rule for a channel/peer.",
ArgsUsage: "{shortchanid | peerpubkey}",
Flags: []cli.Flag{
cli.StringFlag{
&cli.StringFlag{
Name: "type",
Usage: "the type of swap to perform, set to 'out' " +
"for acquiring inbound liquidity or 'in' for " +
@ -56,18 +56,18 @@ var setLiquidityRuleCommand = cli.Command{
Value: "out",
},
cli.IntFlag{
&cli.IntFlag{
Name: "incoming_threshold",
Usage: "the minimum percentage of incoming liquidity " +
"to total capacity beneath which to " +
"recommend loop out to acquire incoming.",
},
cli.IntFlag{
&cli.IntFlag{
Name: "outgoing_threshold",
Usage: "the minimum percentage of outbound liquidity " +
"that we do not want to drop below.",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "clear",
Usage: "remove the rule currently set for the " +
"channel/peer.",
@ -76,9 +76,9 @@ var setLiquidityRuleCommand = cli.Command{
Action: setRule,
}
func setRule(ctx *cli.Context) error {
func setRule(ctx context.Context, cmd *cli.Command) error {
// We require that a channel ID is set for this rule update.
if ctx.NArg() != 1 {
if cmd.NArg() != 1 {
return fmt.Errorf("please set a channel id or peer pubkey " +
"for the rule update")
}
@ -87,9 +87,9 @@ func setRule(ctx *cli.Context) error {
pubkey route.Vertex
pubkeyRule bool
)
chanID, err := strconv.ParseUint(ctx.Args().First(), 10, 64)
chanID, err := strconv.ParseUint(cmd.Args().First(), 10, 64)
if err != nil {
pubkey, err = route.NewVertexFromStr(ctx.Args().First())
pubkey, err = route.NewVertexFromStr(cmd.Args().First())
if err != nil {
return fmt.Errorf("please provide a valid pubkey: "+
"%v, or short channel ID", err)
@ -97,7 +97,7 @@ func setRule(ctx *cli.Context) error {
pubkeyRule = true
}
client, cleanup, err := getClient(ctx)
client, cleanup, err := getClient(ctx, cmd)
if err != nil {
return err
}
@ -107,15 +107,15 @@ func setRule(ctx *cli.Context) error {
// SetParameters. To allow users to set only individual fields on the
// cli, we lookup our current params, then update individual values.
params, err := client.GetLiquidityParams(
context.Background(), &looprpc.GetLiquidityParamsRequest{},
ctx, &looprpc.GetLiquidityParamsRequest{},
)
if err != nil {
return err
}
var (
inboundSet = ctx.IsSet("incoming_threshold")
outboundSet = ctx.IsSet("outgoing_threshold")
inboundSet = cmd.IsSet("incoming_threshold")
outboundSet = cmd.IsSet("outgoing_threshold")
ruleSet bool
otherRules []*looprpc.LiquidityRule
)
@ -144,7 +144,7 @@ func setRule(ctx *cli.Context) error {
// If we want to clear the rule for this channel, check that we had a
// rule set in the first place, and set our parameters to the current
// set excluding the channel specified.
if ctx.IsSet("clear") {
if cmd.IsSet("clear") {
if !ruleSet {
return fmt.Errorf("cannot clear channel: %v, no rule "+
"set at present", chanID)
@ -157,7 +157,7 @@ func setRule(ctx *cli.Context) error {
params.Rules = otherRules
_, err = client.SetLiquidityParams(
context.Background(),
ctx,
&looprpc.SetLiquidityParamsRequest{
Parameters: params,
},
@ -177,8 +177,8 @@ func setRule(ctx *cli.Context) error {
ChannelId: chanID,
Type: looprpc.LiquidityRuleType_THRESHOLD,
}
if ctx.IsSet("type") {
switch ctx.String("type") {
if cmd.IsSet("type") {
switch cmd.String("type") {
case "in":
newRule.SwapType = looprpc.SwapType_LOOP_IN
@ -196,13 +196,13 @@ func setRule(ctx *cli.Context) error {
if inboundSet {
newRule.IncomingThreshold = uint32(
ctx.Int("incoming_threshold"),
cmd.Int("incoming_threshold"),
)
}
if outboundSet {
newRule.OutgoingThreshold = uint32(
ctx.Int("outgoing_threshold"),
cmd.Int("outgoing_threshold"),
)
}
@ -213,7 +213,7 @@ func setRule(ctx *cli.Context) error {
// Update our parameters to the existing set, plus our new rule.
_, err = client.SetLiquidityParams(
context.Background(),
ctx,
&looprpc.SetLiquidityParamsRequest{
Parameters: params,
},
@ -222,7 +222,7 @@ func setRule(ctx *cli.Context) error {
return err
}
var setParamsCommand = cli.Command{
var setParamsCommand = &cli.Command{
Name: "setparams",
Usage: "update the parameters set for the liquidity manager",
Description: "Updates the parameters set for the liquidity manager. " +
@ -230,69 +230,69 @@ var setParamsCommand = cli.Command{
"of setting them again upon loopd restart. To get the default" +
"values, use `getparams` before any `setparams`.",
Flags: []cli.Flag{
cli.IntFlag{
&cli.IntFlag{
Name: "sweeplimit",
Usage: "the limit placed on our estimated sweep fee " +
"in sat/vByte.",
},
cli.Float64Flag{
&cli.Float64Flag{
Name: "feepercent",
Usage: "the maximum percentage of swap amount to be " +
"used across all fee categories",
},
cli.Float64Flag{
&cli.Float64Flag{
Name: "maxswapfee",
Usage: "the maximum percentage of swap volume we are " +
"willing to pay in server fees.",
},
cli.Float64Flag{
&cli.Float64Flag{
Name: "maxroutingfee",
Usage: "the maximum percentage of off-chain payment " +
"volume that we are willing to pay in routing" +
"fees.",
},
cli.Float64Flag{
&cli.Float64Flag{
Name: "maxprepayfee",
Usage: "the maximum percentage of off-chain prepay " +
"volume that we are willing to pay in " +
"routing fees.",
},
cli.Uint64Flag{
&cli.Uint64Flag{
Name: "maxprepay",
Usage: "the maximum no-show (prepay) in satoshis that " +
"swap suggestions should be limited to.",
},
cli.Uint64Flag{
&cli.Uint64Flag{
Name: "maxminer",
Usage: "the maximum miner fee in satoshis that swap " +
"suggestions should be limited to.",
},
cli.IntFlag{
&cli.IntFlag{
Name: "sweepconf",
Usage: "the number of blocks from htlc height that " +
"swap suggestion sweeps should target, used " +
"to estimate max miner fee.",
},
cli.Uint64Flag{
&cli.Uint64Flag{
Name: "failurebackoff",
Usage: "the amount of time, in seconds, that " +
"should pass before a channel that " +
"previously had a failed swap will be " +
"included in suggestions.",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "autoloop",
Usage: "set to true to enable automated dispatch " +
"of swaps, limited to the budget set by " +
"autobudget.",
},
cli.StringFlag{
&cli.StringFlag{
Name: "destaddr",
Usage: "custom address to be used as destination for " +
"autoloop loop out, set to \"default\" in " +
"order to revert to default behavior.",
},
cli.StringFlag{
&cli.StringFlag{
Name: "account",
Usage: "the name of the account to generate a new " +
"address from. You can list the names of " +
@ -300,74 +300,74 @@ var setParamsCommand = cli.Command{
"instance with \"lncli wallet accounts list\".",
Value: "",
},
cli.StringFlag{
&cli.StringFlag{
Name: "account_addr_type",
Usage: "the address type of the extended public key " +
"specified in account. Currently only " +
"pay-to-taproot-pubkey(p2tr) is supported",
Value: "p2tr",
},
cli.Uint64Flag{
&cli.Uint64Flag{
Name: "autobudget",
Usage: "the maximum amount of fees in satoshis that " +
"automatically dispatched loop out swaps may " +
"spend.",
},
cli.DurationFlag{
&cli.DurationFlag{
Name: "autobudgetrefreshperiod",
Usage: "the time period over which the automated " +
"loop budget is refreshed.",
},
cli.Uint64Flag{
&cli.Uint64Flag{
Name: "autoinflight",
Usage: "the maximum number of automatically " +
"dispatched swaps that we allow to be in " +
"flight.",
},
cli.Uint64Flag{
&cli.Uint64Flag{
Name: "minamt",
Usage: "the minimum amount in satoshis that the " +
"autoloop client will dispatch per-swap.",
},
cli.Uint64Flag{
&cli.Uint64Flag{
Name: "maxamt",
Usage: "the maximum amount in satoshis that the " +
"autoloop client will dispatch per-swap.",
},
cli.IntFlag{
&cli.IntFlag{
Name: "htlc_conf",
Usage: "the confirmation target for loop in on-chain " +
"htlcs.",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "easyautoloop",
Usage: "set to true to enable easy autoloop, which " +
"will automatically dispatch swaps in order " +
"to meet the target local balance.",
},
cli.Uint64Flag{
&cli.Uint64Flag{
Name: "localbalancesat",
Usage: "the target size of total local balance in " +
"satoshis, used by easy autoloop.",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "asset_easyautoloop",
Usage: "set to true to enable asset easy autoloop, which " +
"will automatically dispatch asset swaps in order " +
"to meet the target local balance.",
},
cli.StringFlag{
&cli.StringFlag{
Name: "asset_id",
Usage: "If set to a valid asset ID, the easyautoloop " +
"and localbalancesat flags will be set for the " +
"specified asset.",
},
cli.Uint64Flag{
&cli.Uint64Flag{
Name: "asset_localbalance",
Usage: "the target size of total local balance in " +
"asset units, used by asset easy autoloop.",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "fast",
Usage: "if set new swaps are expected to be " +
"published immediately, paying a potentially " +
@ -381,8 +381,8 @@ var setParamsCommand = cli.Command{
Action: setParams,
}
func setParams(ctx *cli.Context) error {
client, cleanup, err := getClient(ctx)
func setParams(ctx context.Context, cmd *cli.Command) error {
client, cleanup, err := getClient(ctx, cmd)
if err != nil {
return err
}
@ -392,7 +392,7 @@ func setParams(ctx *cli.Context) error {
// SetParameters. To allow users to set only individual fields on the
// cli, we lookup our current params, then update individual values.
params, err := client.GetLiquidityParams(
context.Background(), &looprpc.GetLiquidityParamsRequest{},
ctx, &looprpc.GetLiquidityParamsRequest{},
)
if err != nil {
return err
@ -403,8 +403,8 @@ func setParams(ctx *cli.Context) error {
// Update our existing parameters with the values provided by cli flags.
// Our fee categories and fee percentage are exclusive, so track which
// flags are set to ensure that we don't have nonsensical overlap.
if ctx.IsSet("maxswapfee") {
feeRate := ctx.Float64("maxswapfee")
if cmd.IsSet("maxswapfee") {
feeRate := cmd.Float64("maxswapfee")
params.MaxSwapFeePpm, err = ppmFromPercentage(feeRate)
if err != nil {
return err
@ -414,16 +414,16 @@ func setParams(ctx *cli.Context) error {
categoriesSet = true
}
if ctx.IsSet("sweeplimit") {
satPerVByte := ctx.Int("sweeplimit")
if cmd.IsSet("sweeplimit") {
satPerVByte := cmd.Int("sweeplimit")
params.SweepFeeRateSatPerVbyte = uint64(satPerVByte)
flagSet = true
categoriesSet = true
}
if ctx.IsSet("feepercent") {
feeRate := ctx.Float64("feepercent")
if cmd.IsSet("feepercent") {
feeRate := cmd.Float64("feepercent")
params.FeePpm, err = ppmFromPercentage(feeRate)
if err != nil {
return err
@ -433,8 +433,8 @@ func setParams(ctx *cli.Context) error {
feePercentSet = true
}
if ctx.IsSet("maxroutingfee") {
feeRate := ctx.Float64("maxroutingfee")
if cmd.IsSet("maxroutingfee") {
feeRate := cmd.Float64("maxroutingfee")
params.MaxRoutingFeePpm, err = ppmFromPercentage(feeRate)
if err != nil {
return err
@ -444,8 +444,8 @@ func setParams(ctx *cli.Context) error {
categoriesSet = true
}
if ctx.IsSet("maxprepayfee") {
feeRate := ctx.Float64("maxprepayfee")
if cmd.IsSet("maxprepayfee") {
feeRate := cmd.Float64("maxprepayfee")
params.MaxPrepayRoutingFeePpm, err = ppmFromPercentage(feeRate)
if err != nil {
return err
@ -455,59 +455,59 @@ func setParams(ctx *cli.Context) error {
categoriesSet = true
}
if ctx.IsSet("maxprepay") {
params.MaxPrepaySat = ctx.Uint64("maxprepay")
if cmd.IsSet("maxprepay") {
params.MaxPrepaySat = cmd.Uint64("maxprepay")
flagSet = true
categoriesSet = true
}
if ctx.IsSet("maxminer") {
params.MaxMinerFeeSat = ctx.Uint64("maxminer")
if cmd.IsSet("maxminer") {
params.MaxMinerFeeSat = cmd.Uint64("maxminer")
flagSet = true
categoriesSet = true
}
if ctx.IsSet("sweepconf") {
params.SweepConfTarget = int32(ctx.Int("sweepconf"))
if cmd.IsSet("sweepconf") {
params.SweepConfTarget = int32(cmd.Int("sweepconf"))
flagSet = true
}
if ctx.IsSet("failurebackoff") {
params.FailureBackoffSec = ctx.Uint64("failurebackoff")
if cmd.IsSet("failurebackoff") {
params.FailureBackoffSec = cmd.Uint64("failurebackoff")
flagSet = true
}
if ctx.IsSet("autoloop") {
params.Autoloop = ctx.Bool("autoloop")
if cmd.IsSet("autoloop") {
params.Autoloop = cmd.Bool("autoloop")
flagSet = true
}
if ctx.IsSet("autobudget") {
params.AutoloopBudgetSat = ctx.Uint64("autobudget")
if cmd.IsSet("autobudget") {
params.AutoloopBudgetSat = cmd.Uint64("autobudget")
flagSet = true
}
switch {
case ctx.IsSet("destaddr") && ctx.IsSet("account"):
case cmd.IsSet("destaddr") && cmd.IsSet("account"):
return fmt.Errorf("cannot set destaddr and account at the " +
"same time")
case ctx.IsSet("destaddr"):
params.AutoloopDestAddress = ctx.String("destaddr")
case cmd.IsSet("destaddr"):
params.AutoloopDestAddress = cmd.String("destaddr")
params.Account = ""
flagSet = true
case ctx.IsSet("account") != ctx.IsSet("account_addr_type"):
case cmd.IsSet("account") != cmd.IsSet("account_addr_type"):
return liquidity.ErrAccountAndAddrType
case ctx.IsSet("account"):
params.Account = ctx.String("account")
case cmd.IsSet("account"):
params.Account = cmd.String("account")
params.AutoloopDestAddress = ""
flagSet = true
}
if ctx.IsSet("account_addr_type") {
switch ctx.String("account_addr_type") {
if cmd.IsSet("account_addr_type") {
switch cmd.String("account_addr_type") {
case "p2tr":
params.AccountAddrType = looprpc.AddressType_TAPROOT_PUBKEY
@ -516,78 +516,78 @@ func setParams(ctx *cli.Context) error {
}
}
if ctx.IsSet("autobudgetrefreshperiod") {
if cmd.IsSet("autobudgetrefreshperiod") {
params.AutoloopBudgetRefreshPeriodSec =
uint64(ctx.Duration("autobudgetrefreshperiod").Seconds())
uint64(cmd.Duration("autobudgetrefreshperiod").Seconds())
flagSet = true
}
if ctx.IsSet("autoinflight") {
params.AutoMaxInFlight = ctx.Uint64("autoinflight")
if cmd.IsSet("autoinflight") {
params.AutoMaxInFlight = cmd.Uint64("autoinflight")
flagSet = true
}
if ctx.IsSet("minamt") {
params.MinSwapAmount = ctx.Uint64("minamt")
if cmd.IsSet("minamt") {
params.MinSwapAmount = cmd.Uint64("minamt")
flagSet = true
}
if ctx.IsSet("maxamt") {
params.MaxSwapAmount = ctx.Uint64("maxamt")
if cmd.IsSet("maxamt") {
params.MaxSwapAmount = cmd.Uint64("maxamt")
flagSet = true
}
if ctx.IsSet("htlc_conf") {
params.HtlcConfTarget = int32(ctx.Int("htlc_conf"))
if cmd.IsSet("htlc_conf") {
params.HtlcConfTarget = int32(cmd.Int("htlc_conf"))
flagSet = true
}
// If we are setting easy autoloop parameters, we need to ensure that
// the asset ID is set, and that we have a valid entry in our params
// map.
if ctx.IsSet("asset_id") {
if cmd.IsSet("asset_id") {
if params.EasyAssetParams == nil {
params.EasyAssetParams = make(
map[string]*looprpc.EasyAssetAutoloopParams,
)
}
if _, ok := params.EasyAssetParams[ctx.String("asset_id")]; !ok { //nolint:lll
params.EasyAssetParams[ctx.String("asset_id")] =
if _, ok := params.EasyAssetParams[cmd.String("asset_id")]; !ok { //nolint:lll
params.EasyAssetParams[cmd.String("asset_id")] =
&looprpc.EasyAssetAutoloopParams{}
}
}
if ctx.IsSet("easyautoloop") {
params.EasyAutoloop = ctx.Bool("easyautoloop")
if cmd.IsSet("easyautoloop") {
params.EasyAutoloop = cmd.Bool("easyautoloop")
flagSet = true
}
if ctx.IsSet("localbalancesat") {
params.EasyAutoloopLocalTargetSat = ctx.Uint64("localbalancesat")
if cmd.IsSet("localbalancesat") {
params.EasyAutoloopLocalTargetSat = cmd.Uint64("localbalancesat")
flagSet = true
}
if ctx.IsSet("asset_easyautoloop") {
if !ctx.IsSet("asset_id") {
if cmd.IsSet("asset_easyautoloop") {
if !cmd.IsSet("asset_id") {
return fmt.Errorf("asset_id must be set to use " +
"asset_easyautoloop")
}
params.EasyAssetParams[ctx.String("asset_id")].
Enabled = ctx.Bool("asset_easyautoloop")
params.EasyAssetParams[cmd.String("asset_id")].
Enabled = cmd.Bool("asset_easyautoloop")
flagSet = true
}
if ctx.IsSet("asset_localbalance") {
if !ctx.IsSet("asset_id") {
if cmd.IsSet("asset_localbalance") {
if !cmd.IsSet("asset_id") {
return fmt.Errorf("asset_id must be set to use " +
"asset_localbalance")
}
params.EasyAssetParams[ctx.String("asset_id")].
LocalTargetAssetAmt = ctx.Uint64("asset_localbalance")
params.EasyAssetParams[cmd.String("asset_id")].
LocalTargetAssetAmt = cmd.Uint64("asset_localbalance")
flagSet = true
}
if ctx.IsSet("fast") {
if cmd.IsSet("fast") {
params.FastSwapPublication = true
}
@ -619,7 +619,7 @@ func setParams(ctx *cli.Context) error {
}
// Update our parameters to our mutated values.
_, err = client.SetLiquidityParams(
context.Background(), &looprpc.SetLiquidityParamsRequest{
ctx, &looprpc.SetLiquidityParamsRequest{
Parameters: params,
},
)
@ -637,7 +637,7 @@ func ppmFromPercentage(percentage float64) (uint64, error) {
return uint64(percentage / 100 * liquidity.FeeBase), nil
}
var suggestSwapCommand = cli.Command{
var suggestSwapCommand = &cli.Command{
Name: "suggestswaps",
Usage: "show a list of suggested swaps",
Description: "Displays a list of suggested swaps that aim to obtain " +
@ -646,15 +646,15 @@ var suggestSwapCommand = cli.Command{
Action: suggestSwap,
}
func suggestSwap(ctx *cli.Context) error {
client, cleanup, err := getClient(ctx)
func suggestSwap(ctx context.Context, cmd *cli.Command) error {
client, cleanup, err := getClient(ctx, cmd)
if err != nil {
return err
}
defer cleanup()
resp, err := client.SuggestSwaps(
context.Background(), &looprpc.SuggestSwapsRequest{},
ctx, &looprpc.SuggestSwapsRequest{},
)
if err == nil {
printRespJSON(resp)

View file

@ -9,47 +9,47 @@ import (
"github.com/lightninglabs/loop/labels"
"github.com/lightninglabs/loop/looprpc"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/urfave/cli"
"github.com/urfave/cli/v3"
)
var (
lastHopFlag = cli.StringFlag{
lastHopFlag = &cli.StringFlag{
Name: "last_hop",
Usage: "the pubkey of the last hop to use for this swap",
}
confTargetFlag = cli.Uint64Flag{
confTargetFlag = &cli.Uint64Flag{
Name: "conf_target",
Usage: "the target number of blocks the on-chain htlc " +
"broadcast by the swap client should confirm within",
}
labelFlag = cli.StringFlag{
labelFlag = &cli.StringFlag{
Name: "label",
Usage: fmt.Sprintf("an optional label for this swap,"+
"limited to %v characters. The label may not start "+
"with our reserved prefix: %v.",
labels.MaxLength, labels.Reserved),
}
routeHintsFlag = cli.StringSliceFlag{
routeHintsFlag = &cli.StringSliceFlag{
Name: "route_hints",
Usage: "route hints that can each be individually used " +
"to assist in reaching the invoice's destination",
}
privateFlag = cli.BoolFlag{
privateFlag = &cli.BoolFlag{
Name: "private",
Usage: "generates and passes routehints. Should be used if " +
"the connected node is only reachable via private " +
"channels",
}
forceFlag = cli.BoolFlag{
forceFlag = &cli.BoolFlag{
Name: "force, f",
Usage: "Assumes yes during confirmation. Using this option " +
"will result in an immediate swap",
}
loopInCommand = cli.Command{
loopInCommand = &cli.Command{
Name: "in",
Usage: "perform an on-chain to off-chain swap (loop in)",
ArgsUsage: "amt",
@ -66,14 +66,14 @@ var (
conf_target flag.
`,
Flags: []cli.Flag{
cli.Uint64Flag{
&cli.Uint64Flag{
Name: "amt",
Usage: "the amount in satoshis to loop in. " +
"To check for the minimum and " +
"maximum amounts to loop " +
"in please consult \"loop terms\"",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "external",
Usage: "expect htlc to be published externally",
},
@ -89,18 +89,18 @@ var (
}
)
func loopIn(ctx *cli.Context) error {
args := ctx.Args()
func loopIn(ctx context.Context, cmd *cli.Command) error {
args := cmd.Args()
var amtStr string
switch {
case ctx.IsSet("amt"):
amtStr = strconv.FormatUint(ctx.Uint64("amt"), 10)
case ctx.NArg() == 1:
amtStr = args[0]
case cmd.IsSet("amt"):
amtStr = strconv.FormatUint(cmd.Uint64("amt"), 10)
case cmd.NArg() == 1:
amtStr = args.First()
default:
// Show command help if no arguments and flags were provided.
return cli.ShowCommandHelp(ctx, "in")
return showCommandHelp(ctx, cmd)
}
amt, err := parseAmt(amtStr)
@ -108,14 +108,14 @@ func loopIn(ctx *cli.Context) error {
return err
}
client, cleanup, err := getClient(ctx)
client, cleanup, err := getClient(ctx, cmd)
if err != nil {
return err
}
defer cleanup()
external := ctx.Bool("external")
htlcConfTarget := int32(ctx.Uint64(confTargetFlag.Name))
external := cmd.Bool("external")
htlcConfTarget := int32(cmd.Uint64(confTargetFlag.Name))
// External and confirmation target are mutually exclusive; either the
// on chain htlc is being externally broadcast, or we are creating the
@ -126,15 +126,15 @@ func loopIn(ctx *cli.Context) error {
}
// Validate our label early so that we can fail before getting a quote.
label := ctx.String(labelFlag.Name)
label := cmd.String(labelFlag.Name)
if err := labels.Validate(label); err != nil {
return err
}
var lastHop []byte
if ctx.IsSet(lastHopFlag.Name) {
if cmd.IsSet(lastHopFlag.Name) {
lastHopVertex, err := route.NewVertexFromStr(
ctx.String(lastHopFlag.Name),
cmd.String(lastHopFlag.Name),
)
if err != nil {
return err
@ -145,7 +145,7 @@ func loopIn(ctx *cli.Context) error {
// Private and routehints are mutually exclusive as setting private
// means we retrieve our own routehints from the connected node.
hints, err := validateRouteHints(ctx)
hints, err := validateRouteHints(cmd)
if err != nil {
return err
}
@ -156,10 +156,10 @@ func loopIn(ctx *cli.Context) error {
ExternalHtlc: external,
LoopInLastHop: lastHop,
LoopInRouteHints: hints,
Private: ctx.Bool(privateFlag.Name),
Private: cmd.Bool(privateFlag.Name),
}
quote, err := client.GetLoopInQuote(context.Background(), quoteReq)
quote, err := client.GetLoopInQuote(ctx, quoteReq)
if err != nil {
return err
}
@ -181,8 +181,8 @@ func loopIn(ctx *cli.Context) error {
limits := getInLimits(quote)
// Skip showing details if configured
if !(ctx.Bool("force") || ctx.Bool("f")) {
err = displayInDetails(quoteReq, quote, ctx.Bool("verbose"))
if !(cmd.Bool("force") || cmd.Bool("f")) {
err = displayInDetails(quoteReq, quote, cmd.Bool("verbose"))
if err != nil {
return err
}
@ -198,10 +198,10 @@ func loopIn(ctx *cli.Context) error {
Initiator: defaultInitiator,
LastHop: lastHop,
RouteHints: hints,
Private: ctx.Bool(privateFlag.Name),
Private: cmd.Bool(privateFlag.Name),
}
resp, err := client.LoopIn(context.Background(), req)
resp, err := client.LoopIn(ctx, req)
if err != nil {
return err
}

View file

@ -14,17 +14,17 @@ import (
"github.com/lightninglabs/loop/labels"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/looprpc"
"github.com/urfave/cli"
"github.com/urfave/cli/v3"
)
var (
channelFlag = cli.StringFlag{
channelFlag = &cli.StringFlag{
Name: "channel",
Usage: "the comma-separated list of short " +
"channel IDs of the channels to loop out",
}
)
var loopOutCommand = cli.Command{
var loopOutCommand = &cli.Command{
Name: "out",
Usage: "perform an off-chain to on-chain swap (looping out)",
ArgsUsage: "amt [addr]",
@ -37,13 +37,13 @@ var loopOutCommand = cli.Command{
Optionally a BASE58/bech32 encoded bitcoin destination address may be
specified. If not specified, a new wallet address will be generated.`,
Flags: []cli.Flag{
cli.StringFlag{
&cli.StringFlag{
Name: "addr",
Usage: "the optional address that the looped out funds " +
"should be sent to, if let blank the funds " +
"will go to lnd's wallet",
},
cli.StringFlag{
&cli.StringFlag{
Name: "account",
Usage: "the name of the account to generate a new " +
"address from. You can list the names of " +
@ -51,40 +51,40 @@ var loopOutCommand = cli.Command{
"instance with \"lncli wallet accounts list\"",
Value: "",
},
cli.StringFlag{
&cli.StringFlag{
Name: "account_addr_type",
Usage: "the address type of the extended public key " +
"specified in account. Currently only " +
"pay-to-taproot-pubkey(p2tr) is supported",
Value: "p2tr",
},
cli.Uint64Flag{
&cli.Uint64Flag{
Name: "amt",
Usage: "the amount in satoshis to loop out. To check " +
"for the minimum and maximum amounts to loop " +
"out please consult \"loop terms\"",
},
cli.Uint64Flag{
&cli.Uint64Flag{
Name: "htlc_confs",
Usage: "the number of confirmations (in blocks) " +
"that we require for the htlc extended by " +
"the server before we reveal the preimage",
Value: uint64(loopdb.DefaultLoopOutHtlcConfirmations),
},
cli.Uint64Flag{
&cli.Uint64Flag{
Name: "conf_target",
Usage: "the number of blocks from the swap " +
"initiation height that the on-chain HTLC " +
"should be swept within",
Value: uint64(loop.DefaultSweepConfTarget),
},
cli.Int64Flag{
&cli.Int64Flag{
Name: "max_swap_routing_fee",
Usage: "the max off-chain swap routing fee in " +
"satoshis, if not specified, a default max " +
"fee will be used",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "fast",
Usage: "indicate you want to swap immediately, " +
"paying potentially a higher fee. If not " +
@ -94,7 +94,7 @@ var loopOutCommand = cli.Command{
"Not setting this flag therefore might " +
"result in a lower swap fee",
},
cli.DurationFlag{
&cli.DurationFlag{
Name: "payment_timeout",
Usage: "the timeout for each individual off-chain " +
"payment attempt. If not set, the default " +
@ -102,14 +102,14 @@ var loopOutCommand = cli.Command{
"payment might be retried, the actual total " +
"time may be longer",
},
cli.StringFlag{
&cli.StringFlag{
Name: "asset_id",
Usage: "the asset ID of the asset to loop out, " +
"if this is set, the loop daemon will " +
"require a connection to a taproot assets " +
"daemon",
},
cli.StringFlag{
&cli.StringFlag{
Name: "asset_edge_node",
Usage: "the pubkey of the edge node of the asset to " +
"loop out, this is required if the taproot " +
@ -124,19 +124,22 @@ var loopOutCommand = cli.Command{
Action: loopOut,
}
func loopOut(ctx *cli.Context) error {
args := ctx.Args()
func loopOut(ctx context.Context, cmd *cli.Command) error {
args := cmd.Args()
var amtStr string
var (
amtStr string
remaining []string
)
switch {
case ctx.IsSet("amt"):
amtStr = strconv.FormatUint(ctx.Uint64("amt"), 10)
case ctx.NArg() == 1 || ctx.NArg() == 2:
amtStr = args[0]
args = args.Tail()
case cmd.IsSet("amt"):
amtStr = strconv.FormatUint(cmd.Uint64("amt"), 10)
case cmd.NArg() == 1 || cmd.NArg() == 2:
amtStr = args.First()
remaining = args.Tail()
default:
// Show command help if no arguments and flags were provided.
return cli.ShowCommandHelp(ctx, "out")
return showCommandHelp(ctx, cmd)
}
amt, err := parseAmt(amtStr)
@ -148,12 +151,12 @@ func loopOut(ctx *cli.Context) error {
// Otherwise, strings.Split returns a slice of length one with an empty
// element.
var outgoingChanSet []uint64
if ctx.IsSet("channel") {
if ctx.IsSet("asset_id") {
if cmd.IsSet("channel") {
if cmd.IsSet("asset_id") {
return fmt.Errorf("channel flag is not supported when " +
"looping out assets")
}
chanStrings := strings.Split(ctx.String("channel"), ",")
chanStrings := strings.Split(cmd.String("channel"), ",")
for _, chanString := range chanStrings {
chanID, err := strconv.ParseUint(chanString, 10, 64)
if err != nil {
@ -165,12 +168,12 @@ func loopOut(ctx *cli.Context) error {
}
// Validate our label early so that we can fail before getting a quote.
label := ctx.String(labelFlag.Name)
label := cmd.String(labelFlag.Name)
if err := labels.Validate(label); err != nil {
return err
}
if ctx.IsSet("addr") && ctx.IsSet("account") {
if cmd.IsSet("addr") && cmd.IsSet("account") {
return fmt.Errorf("cannot set --addr and --account at the " +
"same time. Please specify only one source for a new " +
"address to sweep the loop amount to")
@ -181,24 +184,24 @@ func loopOut(ctx *cli.Context) error {
account string
)
switch {
case ctx.IsSet("addr"):
destAddr = ctx.String("addr")
case cmd.IsSet("addr"):
destAddr = cmd.String("addr")
case ctx.IsSet("account"):
account = ctx.String("account")
case cmd.IsSet("account"):
account = cmd.String("account")
case args.Present():
destAddr = args.First()
case len(remaining) > 0:
destAddr = remaining[0]
}
if ctx.IsSet("account") != ctx.IsSet("account_addr_type") {
if cmd.IsSet("account") != cmd.IsSet("account_addr_type") {
return fmt.Errorf("cannot set account without specifying " +
"account address type and vice versa")
}
var accountAddrType looprpc.AddressType
if ctx.IsSet("account_addr_type") {
switch ctx.String("account_addr_type") {
if cmd.IsSet("account_addr_type") {
switch cmd.String("account_addr_type") {
case "p2tr":
accountAddrType = looprpc.AddressType_TAPROOT_PUBKEY
@ -210,19 +213,19 @@ func loopOut(ctx *cli.Context) error {
var assetLoopOutInfo *looprpc.AssetLoopOutRequest
var assetId []byte
if ctx.IsSet("asset_id") {
if !ctx.IsSet("asset_edge_node") {
if cmd.IsSet("asset_id") {
if !cmd.IsSet("asset_edge_node") {
return fmt.Errorf("asset edge node is required when " +
"assetid is set")
}
assetId, err = hex.DecodeString(ctx.String("asset_id"))
assetId, err = hex.DecodeString(cmd.String("asset_id"))
if err != nil {
return err
}
assetEdgeNode, err := hex.DecodeString(
ctx.String("asset_edge_node"),
cmd.String("asset_edge_node"),
)
if err != nil {
return err
@ -234,7 +237,7 @@ func loopOut(ctx *cli.Context) error {
}
}
client, cleanup, err := getClient(ctx)
client, cleanup, err := getClient(ctx, cmd)
if err != nil {
return err
}
@ -242,14 +245,14 @@ func loopOut(ctx *cli.Context) error {
// Set our maximum swap wait time. If a fast swap is requested we set
// it to now, otherwise to 30 minutes in the future.
fast := ctx.Bool("fast")
fast := cmd.Bool("fast")
swapDeadline := time.Now()
if !fast {
swapDeadline = time.Now().Add(defaultSwapWaitTime)
}
sweepConfTarget := int32(ctx.Uint64("conf_target"))
htlcConfs := int32(ctx.Uint64("htlc_confs"))
sweepConfTarget := int32(cmd.Uint64("conf_target"))
htlcConfs := int32(cmd.Uint64("htlc_confs"))
if htlcConfs == 0 {
return fmt.Errorf("at least 1 confirmation required for htlcs")
}
@ -260,7 +263,7 @@ func loopOut(ctx *cli.Context) error {
SwapPublicationDeadline: uint64(swapDeadline.Unix()),
AssetInfo: assetLoopOutInfo,
}
quote, err := client.LoopOutQuote(context.Background(), quoteReq)
quote, err := client.LoopOutQuote(ctx, quoteReq)
if err != nil {
return err
}
@ -277,16 +280,16 @@ func loopOut(ctx *cli.Context) error {
limits := getOutLimits(amt, quote)
// If configured, use the specified maximum swap routing fee.
if ctx.IsSet("max_swap_routing_fee") {
if cmd.IsSet("max_swap_routing_fee") {
limits.maxSwapRoutingFee = btcutil.Amount(
ctx.Int64("max_swap_routing_fee"),
cmd.Int64("max_swap_routing_fee"),
)
}
// Skip showing details if configured
if !(ctx.Bool("force") || ctx.Bool("f")) {
if !(cmd.Bool("force") || cmd.Bool("f")) {
err = displayOutDetails(
limits, warning, quoteReq, quote, ctx.Bool("verbose"),
limits, warning, quoteReq, quote, cmd.Bool("verbose"),
)
if err != nil {
return err
@ -294,8 +297,8 @@ func loopOut(ctx *cli.Context) error {
}
var paymentTimeout int64
if ctx.IsSet("payment_timeout") {
parsedTimeout := ctx.Duration("payment_timeout")
if cmd.IsSet("payment_timeout") {
parsedTimeout := cmd.Duration("payment_timeout")
if parsedTimeout.Truncate(time.Second) != parsedTimeout {
return fmt.Errorf("payment timeout must be a " +
"whole number of seconds")
@ -312,7 +315,7 @@ func loopOut(ctx *cli.Context) error {
}
}
resp, err := client.LoopOut(context.Background(), &looprpc.LoopOutRequest{
resp, err := client.LoopOut(ctx, &looprpc.LoopOutRequest{
Amt: int64(amt),
Dest: destAddr,
IsExternalAddr: destAddr != "",

View file

@ -2,6 +2,7 @@ package main
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
@ -21,7 +22,7 @@ import (
"github.com/lightningnetwork/lnd/lncfg"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/macaroons"
"github.com/urfave/cli"
"github.com/urfave/cli/v3"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/protobuf/proto"
@ -50,38 +51,39 @@ var (
// user agent string we send when using the command line utility.
defaultInitiator = "loop-cli"
loopDirFlag = cli.StringFlag{
Name: "loopdir",
Value: loopd.LoopDirBase,
Usage: "path to loop's base directory",
EnvVar: envVarLoopDir,
loopDirFlag = &cli.StringFlag{
Name: "loopdir",
Value: loopd.LoopDirBase,
Usage: "path to loop's base directory",
Sources: cli.EnvVars(envVarLoopDir),
}
networkFlag = cli.StringFlag{
Name: "network, n",
Usage: "the network loop is running on e.g. mainnet, " +
"testnet, etc.",
Value: loopd.DefaultNetwork,
EnvVar: envVarNetwork,
networkFlag = &cli.StringFlag{
Name: "network",
Aliases: []string{"n"},
Usage: "the network loop is running on e.g. mainnet, testnet, etc.",
Value: loopd.DefaultNetwork,
Sources: cli.EnvVars(envVarNetwork),
}
tlsCertFlag = cli.StringFlag{
Name: "tlscertpath",
Usage: "path to loop's TLS certificate",
Value: loopd.DefaultTLSCertPath,
EnvVar: envVarTLSCertPath,
tlsCertFlag = &cli.StringFlag{
Name: "tlscertpath",
Usage: "path to loop's TLS certificate",
Value: loopd.DefaultTLSCertPath,
Sources: cli.EnvVars(envVarTLSCertPath),
}
macaroonPathFlag = cli.StringFlag{
Name: "macaroonpath",
Usage: "path to macaroon file",
Value: loopd.DefaultMacaroonPath,
EnvVar: envVarMacaroonPath,
macaroonPathFlag = &cli.StringFlag{
Name: "macaroonpath",
Usage: "path to macaroon file",
Value: loopd.DefaultMacaroonPath,
Sources: cli.EnvVars(envVarMacaroonPath),
}
verboseFlag = cli.BoolFlag{
Name: "verbose, v",
Usage: "show expanded details",
verboseFlag = &cli.BoolFlag{
Name: "verbose",
Aliases: []string{"v"},
Usage: "show expanded details",
}
commands = []cli.Command{
commands = []*cli.Command{
loopOutCommand, loopInCommand, termsCommand,
monitorCommand, quoteCommand, listAuthCommand, fetchL402Command,
listSwapsCommand, swapInfoCommand, getLiquidityParamsCommand,
@ -160,38 +162,40 @@ func fatal(err error) {
}
func main() {
app := cli.NewApp()
app.Version = loop.RichVersion()
app.Name = "loop"
app.Usage = "control plane for your loopd"
app.Flags = []cli.Flag{
cli.StringFlag{
Name: "rpcserver",
Value: "localhost:11010",
Usage: "loopd daemon address host:port",
EnvVar: envVarRPCServer,
rootCmd := &cli.Command{
Name: "loop",
Usage: "control plane for your loopd",
Version: loop.RichVersion(),
Flags: []cli.Flag{
&cli.StringFlag{
Name: "rpcserver",
Value: "localhost:11010",
Usage: "loopd daemon address host:port",
Sources: cli.EnvVars(envVarRPCServer),
},
networkFlag,
loopDirFlag,
tlsCertFlag,
macaroonPathFlag,
},
Commands: commands,
Action: func(ctx context.Context, cmd *cli.Command) error {
return cli.ShowRootCommandHelp(cmd)
},
networkFlag,
loopDirFlag,
tlsCertFlag,
macaroonPathFlag,
}
app.Commands = commands
err := app.Run(os.Args)
if err != nil {
if err := rootCmd.Run(context.Background(), os.Args); err != nil {
fatal(err)
}
}
func getClient(ctx *cli.Context) (looprpc.SwapClientClient, func(), error) {
rpcServer := ctx.GlobalString("rpcserver")
tlsCertPath, macaroonPath, err := extractPathArgs(ctx)
func getClient(ctx context.Context, cmd *cli.Command) (looprpc.SwapClientClient, func(), error) {
rpcServer := cmd.String("rpcserver")
tlsCertPath, macaroonPath, err := extractPathArgs(cmd)
if err != nil {
return nil, nil, err
}
conn, err := getClientConn(rpcServer, tlsCertPath, macaroonPath)
conn, err := getClientConn(ctx, rpcServer, tlsCertPath, macaroonPath)
if err != nil {
return nil, nil, err
}
@ -207,11 +211,11 @@ func getMaxRoutingFee(amt btcutil.Amount) btcutil.Amount {
// extractPathArgs parses the TLS certificate and macaroon paths from the
// command.
func extractPathArgs(ctx *cli.Context) (string, string, error) {
func extractPathArgs(cmd *cli.Command) (string, string, error) {
// We'll start off by parsing the network. This is needed to determine
// the correct path to the TLS certificate and macaroon when not
// specified.
networkStr := strings.ToLower(ctx.GlobalString("network"))
networkStr := strings.ToLower(cmd.String("network"))
_, err := lndclient.Network(networkStr).ChainParams()
if err != nil {
return "", "", err
@ -220,12 +224,12 @@ func extractPathArgs(ctx *cli.Context) (string, string, error) {
// We'll now fetch the loopdir so we can make a decision on how to
// properly read the macaroons and also the cert. This will either be
// the default, or will have been overwritten by the end user.
loopDir := lncfg.CleanAndExpandPath(ctx.GlobalString(loopDirFlag.Name))
loopDir := lncfg.CleanAndExpandPath(cmd.String(loopDirFlag.Name))
tlsCertPathRaw := ctx.GlobalString(tlsCertFlag.Name)
tlsCertPathRaw := cmd.String(tlsCertFlag.Name)
tlsCertPath := lncfg.CleanAndExpandPath(tlsCertPathRaw)
macPathRaw := ctx.GlobalString(macaroonPathFlag.Name)
macPathRaw := cmd.String(macaroonPathFlag.Name)
macPath := lncfg.CleanAndExpandPath(macPathRaw)
// If a custom loop directory or network was set, we'll also check if
@ -410,7 +414,7 @@ func logSwap(swap *looprpc.SwapStatus) {
fmt.Println()
}
func getClientConn(address, tlsCertPath, macaroonPath string) (*grpc.ClientConn,
func getClientConn(ctx context.Context, address, tlsCertPath, macaroonPath string) (*grpc.ClientConn,
error) {
// We always need to send a macaroon.
@ -432,7 +436,7 @@ func getClientConn(address, tlsCertPath, macaroonPath string) (*grpc.ClientConn,
opts = append(opts, grpc.WithTransportCredentials(creds))
conn, err := grpc.Dial(address, opts...)
conn, err := grpc.DialContext(ctx, address, opts...)
if err != nil {
return nil, fmt.Errorf("unable to connect to RPC server: %v",
err)

View file

@ -5,25 +5,25 @@ import (
"fmt"
"github.com/lightninglabs/loop/looprpc"
"github.com/urfave/cli"
"github.com/urfave/cli/v3"
)
var monitorCommand = cli.Command{
var monitorCommand = &cli.Command{
Name: "monitor",
Usage: "monitor progress of any active swaps",
Description: "Allows the user to monitor progress of any active swaps",
Action: monitor,
}
func monitor(ctx *cli.Context) error {
client, cleanup, err := getClient(ctx)
func monitor(ctx context.Context, cmd *cli.Command) error {
client, cleanup, err := getClient(ctx, cmd)
if err != nil {
return err
}
defer cleanup()
stream, err := client.Monitor(
context.Background(), &looprpc.MonitorRequest{})
ctx, &looprpc.MonitorRequest{})
if err != nil {
return err
}

View file

@ -14,23 +14,23 @@ import (
"github.com/lightninglabs/taproot-assets/taprpc/rfqrpc"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/urfave/cli"
"github.com/urfave/cli/v3"
)
var quoteCommand = cli.Command{
Name: "quote",
Usage: "get a quote for the cost of a swap",
Subcommands: []cli.Command{quoteInCommand, quoteOutCommand},
var quoteCommand = &cli.Command{
Name: "quote",
Usage: "get a quote for the cost of a swap",
Commands: []*cli.Command{quoteInCommand, quoteOutCommand},
}
var quoteInCommand = cli.Command{
var quoteInCommand = &cli.Command{
Name: "in",
Usage: "get a quote for the cost of a loop in swap",
ArgsUsage: "amt",
Description: "Allows to determine the cost of a swap up front." +
"Either specify an amount or deposit outpoints.",
Flags: []cli.Flag{
cli.StringFlag{
&cli.StringFlag{
Name: lastHopFlag.Name,
Usage: "the pubkey of the last hop to use for the " +
"quote",
@ -39,7 +39,7 @@ var quoteInCommand = cli.Command{
verboseFlag,
privateFlag,
routeHintsFlag,
cli.StringSliceFlag{
&cli.StringSliceFlag{
Name: "deposit_outpoint",
Usage: "one or more static address deposit outpoints " +
"to quote for. Deposit outpoints are not to " +
@ -51,10 +51,10 @@ var quoteInCommand = cli.Command{
Action: quoteIn,
}
func quoteIn(ctx *cli.Context) error {
func quoteIn(ctx context.Context, cmd *cli.Command) error {
// Show command help if the incorrect number arguments was provided.
if ctx.NArg() != 1 && !ctx.IsSet("deposit_outpoint") {
return cli.ShowCommandHelp(ctx, "in")
if cmd.NArg() != 1 && !cmd.IsSet("deposit_outpoint") {
return showCommandHelp(ctx, cmd)
}
var (
@ -62,18 +62,17 @@ func quoteIn(ctx *cli.Context) error {
depositAmt btcutil.Amount
depositOutpoints []string
err error
ctxb = context.Background()
)
if ctx.NArg() == 1 {
args := ctx.Args()
manualAmt, err = parseAmt(args[0])
if cmd.NArg() == 1 {
args := cmd.Args()
manualAmt, err = parseAmt(args.First())
if err != nil {
return err
}
}
client, cleanup, err := getClient(ctx)
client, cleanup, err := getClient(ctx, cmd)
if err != nil {
return err
}
@ -81,14 +80,14 @@ func quoteIn(ctx *cli.Context) error {
// Private and routehints are mutually exclusive as setting private
// means we retrieve our own routehints from the connected node.
hints, err := validateRouteHints(ctx)
hints, err := validateRouteHints(cmd)
if err != nil {
return err
}
if ctx.IsSet("deposit_outpoint") {
depositOutpoints = ctx.StringSlice("deposit_outpoint")
depositAmt, err = depositAmount(ctxb, client, depositOutpoints)
if cmd.IsSet("deposit_outpoint") {
depositOutpoints = cmd.StringSlice("deposit_outpoint")
depositAmt, err = depositAmount(ctx, client, depositOutpoints)
if err != nil {
return err
}
@ -96,15 +95,15 @@ func quoteIn(ctx *cli.Context) error {
quoteReq := &looprpc.QuoteRequest{
Amt: int64(manualAmt),
ConfTarget: int32(ctx.Uint64("conf_target")),
ConfTarget: int32(cmd.Uint64("conf_target")),
LoopInRouteHints: hints,
Private: ctx.Bool(privateFlag.Name),
Private: cmd.Bool(privateFlag.Name),
DepositOutpoints: depositOutpoints,
}
if ctx.IsSet(lastHopFlag.Name) {
if cmd.IsSet(lastHopFlag.Name) {
lastHopVertex, err := route.NewVertexFromStr(
ctx.String(lastHopFlag.Name),
cmd.String(lastHopFlag.Name),
)
if err != nil {
return err
@ -113,7 +112,7 @@ func quoteIn(ctx *cli.Context) error {
quoteReq.LoopInLastHop = lastHopVertex[:]
}
quoteResp, err := client.GetLoopInQuote(ctxb, quoteReq)
quoteResp, err := client.GetLoopInQuote(ctx, quoteReq)
if err != nil {
return err
}
@ -136,7 +135,7 @@ func quoteIn(ctx *cli.Context) error {
if manualAmt == 0 {
quoteReq.Amt = int64(depositAmt)
}
printQuoteInResp(quoteReq, quoteResp, ctx.Bool("verbose"))
printQuoteInResp(quoteReq, quoteResp, cmd.Bool("verbose"))
return nil
}
@ -160,20 +159,20 @@ func depositAmount(ctx context.Context, client looprpc.SwapClientClient,
return depositAmt, nil
}
var quoteOutCommand = cli.Command{
var quoteOutCommand = &cli.Command{
Name: "out",
Usage: "get a quote for the cost of a loop out swap",
ArgsUsage: "amt",
Description: "Allows to determine the cost of a swap up front",
Flags: []cli.Flag{
cli.Uint64Flag{
&cli.Uint64Flag{
Name: "conf_target",
Usage: "the number of blocks from the swap " +
"initiation height that the on-chain HTLC " +
"should be swept within in a Loop Out",
Value: uint64(loop.DefaultSweepConfTarget),
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "fast",
Usage: "Indicate you want to swap immediately, " +
"paying potentially a higher fee. If not " +
@ -188,42 +187,41 @@ var quoteOutCommand = cli.Command{
Action: quoteOut,
}
func quoteOut(ctx *cli.Context) error {
func quoteOut(ctx context.Context, cmd *cli.Command) error {
// Show command help if the incorrect number arguments was provided.
if ctx.NArg() != 1 {
return cli.ShowCommandHelp(ctx, "out")
if cmd.NArg() != 1 {
return showCommandHelp(ctx, cmd)
}
args := ctx.Args()
amt, err := parseAmt(args[0])
args := cmd.Args()
amt, err := parseAmt(args.First())
if err != nil {
return err
}
client, cleanup, err := getClient(ctx)
client, cleanup, err := getClient(ctx, cmd)
if err != nil {
return err
}
defer cleanup()
fast := ctx.Bool("fast")
fast := cmd.Bool("fast")
swapDeadline := time.Now()
if !fast {
swapDeadline = time.Now().Add(defaultSwapWaitTime)
}
ctxb := context.Background()
quoteReq := &looprpc.QuoteRequest{
Amt: int64(amt),
ConfTarget: int32(ctx.Uint64("conf_target")),
ConfTarget: int32(cmd.Uint64("conf_target")),
SwapPublicationDeadline: uint64(swapDeadline.Unix()),
}
quoteResp, err := client.LoopOutQuote(ctxb, quoteReq)
quoteResp, err := client.LoopOutQuote(ctx, quoteReq)
if err != nil {
return err
}
printQuoteOutResp(quoteReq, quoteResp, ctx.Bool("verbose"))
printQuoteOutResp(quoteReq, quoteResp, cmd.Bool("verbose"))
return nil
}

View file

@ -4,29 +4,29 @@ import (
"context"
"github.com/lightninglabs/loop/looprpc"
"github.com/urfave/cli"
"github.com/urfave/cli/v3"
)
var reservationsCommands = cli.Command{
var reservationsCommands = &cli.Command{
Name: "reservations",
ShortName: "r",
Usage: "manage reservations",
Name: "reservations",
Aliases: []string{"r"},
Usage: "manage reservations",
Description: `
With loopd running, you can use this command to manage your
reservations. Reservations are 2-of-2 multisig utxos that
the loop server can open to clients. The reservations are used
to enable instant swaps.
`,
Subcommands: []cli.Command{
Commands: []*cli.Command{
listReservationsCommand,
},
}
var (
listReservationsCommand = cli.Command{
listReservationsCommand = &cli.Command{
Name: "list",
ShortName: "l",
Aliases: []string{"l"},
Usage: "list all reservations",
ArgsUsage: "",
Description: `
@ -36,15 +36,15 @@ var (
}
)
func listReservations(ctx *cli.Context) error {
client, cleanup, err := getClient(ctx)
func listReservations(ctx context.Context, cmd *cli.Command) error {
client, cleanup, err := getClient(ctx, cmd)
if err != nil {
return err
}
defer cleanup()
resp, err := client.ListReservations(
context.Background(), &looprpc.ListReservationsRequest{},
ctx, &looprpc.ListReservationsRequest{},
)
if err != nil {
return err

View file

@ -15,18 +15,18 @@ import (
"github.com/lightninglabs/loop/staticaddr/loopin"
"github.com/lightninglabs/loop/swapserverrpc"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/urfave/cli"
"github.com/urfave/cli/v3"
)
func init() {
commands = append(commands, staticAddressCommands)
}
var staticAddressCommands = cli.Command{
Name: "static",
ShortName: "s",
Usage: "perform on-chain to off-chain swaps using static addresses.",
Subcommands: []cli.Command{
var staticAddressCommands = &cli.Command{
Name: "static",
Aliases: []string{"s"},
Usage: "perform on-chain to off-chain swaps using static addresses.",
Commands: []*cli.Command{
newStaticAddressCommand,
listUnspentCommand,
listDepositsCommand,
@ -38,10 +38,10 @@ var staticAddressCommands = cli.Command{
},
}
var newStaticAddressCommand = cli.Command{
Name: "new",
ShortName: "n",
Usage: "Create a new static loop in address.",
var newStaticAddressCommand = &cli.Command{
Name: "new",
Aliases: []string{"n"},
Usage: "Create a new static loop in address.",
Description: `
Requests a new static loop in address from the server. Funds that are
sent to this address will be locked by a 2:2 multisig between us and the
@ -52,10 +52,9 @@ var newStaticAddressCommand = cli.Command{
Action: newStaticAddress,
}
func newStaticAddress(ctx *cli.Context) error {
ctxb := context.Background()
if ctx.NArg() > 0 {
return cli.ShowCommandHelp(ctx, "new")
func newStaticAddress(ctx context.Context, cmd *cli.Command) error {
if cmd.NArg() > 0 {
return showCommandHelp(ctx, cmd)
}
err := displayNewAddressWarning()
@ -63,14 +62,14 @@ func newStaticAddress(ctx *cli.Context) error {
return err
}
client, cleanup, err := getClient(ctx)
client, cleanup, err := getClient(ctx, cmd)
if err != nil {
return err
}
defer cleanup()
resp, err := client.NewStaticAddress(
ctxb, &looprpc.NewStaticAddressRequest{},
ctx, &looprpc.NewStaticAddressRequest{},
)
if err != nil {
return err
@ -81,20 +80,20 @@ func newStaticAddress(ctx *cli.Context) error {
return nil
}
var listUnspentCommand = cli.Command{
Name: "listunspent",
ShortName: "l",
Usage: "List unspent static address outputs.",
var listUnspentCommand = &cli.Command{
Name: "listunspent",
Aliases: []string{"l"},
Usage: "List unspent static address outputs.",
Description: `
List all unspent static address outputs.
`,
Flags: []cli.Flag{
cli.IntFlag{
&cli.IntFlag{
Name: "min_confs",
Usage: "The minimum amount of confirmations an " +
"output should have to be listed.",
},
cli.IntFlag{
&cli.IntFlag{
Name: "max_confs",
Usage: "The maximum number of confirmations an " +
"output could have to be listed.",
@ -103,22 +102,21 @@ var listUnspentCommand = cli.Command{
Action: listUnspent,
}
func listUnspent(ctx *cli.Context) error {
ctxb := context.Background()
if ctx.NArg() > 0 {
return cli.ShowCommandHelp(ctx, "listunspent")
func listUnspent(ctx context.Context, cmd *cli.Command) error {
if cmd.NArg() > 0 {
return showCommandHelp(ctx, cmd)
}
client, cleanup, err := getClient(ctx)
client, cleanup, err := getClient(ctx, cmd)
if err != nil {
return err
}
defer cleanup()
resp, err := client.ListUnspentDeposits(
ctxb, &looprpc.ListUnspentDepositsRequest{
MinConfs: int32(ctx.Int("min_confs")),
MaxConfs: int32(ctx.Int("max_confs")),
ctx, &looprpc.ListUnspentDepositsRequest{
MinConfs: int32(cmd.Int("min_confs")),
MaxConfs: int32(cmd.Int("max_confs")),
})
if err != nil {
return err
@ -129,37 +127,37 @@ func listUnspent(ctx *cli.Context) error {
return nil
}
var withdrawalCommand = cli.Command{
Name: "withdraw",
ShortName: "w",
Usage: "Withdraw from static address deposits.",
var withdrawalCommand = &cli.Command{
Name: "withdraw",
Aliases: []string{"w"},
Usage: "Withdraw from static address deposits.",
Description: `
Withdraws from all or selected static address deposits by sweeping them
to the internal wallet or an external address.
`,
Flags: []cli.Flag{
cli.StringSliceFlag{
&cli.StringSliceFlag{
Name: "utxo",
Usage: "specify utxos as outpoints(tx:idx) which will" +
"be withdrawn.",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "all",
Usage: "withdraws all static address deposits.",
},
cli.StringFlag{
&cli.StringFlag{
Name: "dest_addr",
Usage: "the optional address that the withdrawn " +
"funds should be sent to, if let blank the " +
"funds will go to lnd's wallet",
},
cli.Int64Flag{
&cli.Int64Flag{
Name: "sat_per_vbyte",
Usage: "(optional) a manual fee expressed in " +
"sat/vbyte that should be used when crafting " +
"the transaction",
},
cli.IntFlag{
&cli.IntFlag{
Name: "amount",
Usage: "the number of satoshis that should be " +
"withdrawn from the selected deposits. The " +
@ -169,22 +167,21 @@ var withdrawalCommand = cli.Command{
Action: withdraw,
}
func withdraw(ctx *cli.Context) error {
if ctx.NArg() > 0 {
return cli.ShowCommandHelp(ctx, "withdraw")
func withdraw(ctx context.Context, cmd *cli.Command) error {
if cmd.NArg() > 0 {
return showCommandHelp(ctx, cmd)
}
client, cleanup, err := getClient(ctx)
client, cleanup, err := getClient(ctx, cmd)
if err != nil {
return err
}
defer cleanup()
var (
isAllSelected = ctx.IsSet("all")
isUtxoSelected = ctx.IsSet("utxo")
isAllSelected = cmd.IsSet("all")
isUtxoSelected = cmd.IsSet("utxo")
outpoints []*looprpc.OutPoint
ctxb = context.Background()
destAddr string
)
@ -194,7 +191,7 @@ func withdraw(ctx *cli.Context) error {
case isAllSelected:
case isUtxoSelected:
utxos := ctx.StringSlice("utxo")
utxos := cmd.StringSlice("utxo")
outpoints, err = utxosToOutpoints(utxos)
if err != nil {
return err
@ -204,17 +201,17 @@ func withdraw(ctx *cli.Context) error {
return fmt.Errorf("unknown withdrawal request")
}
if ctx.IsSet("dest_addr") {
destAddr = ctx.String("dest_addr")
if cmd.IsSet("dest_addr") {
destAddr = cmd.String("dest_addr")
}
resp, err := client.WithdrawDeposits(ctxb,
resp, err := client.WithdrawDeposits(ctx,
&looprpc.WithdrawDepositsRequest{
Outpoints: outpoints,
All: isAllSelected,
DestAddr: destAddr,
SatPerVbyte: int64(ctx.Uint64("sat_per_vbyte")),
Amount: ctx.Int64("amount"),
SatPerVbyte: int64(cmd.Uint64("sat_per_vbyte")),
Amount: cmd.Int64("amount"),
})
if err != nil {
return err
@ -225,14 +222,14 @@ func withdraw(ctx *cli.Context) error {
return nil
}
var listDepositsCommand = cli.Command{
var listDepositsCommand = &cli.Command{
Name: "listdeposits",
Usage: "Displays static address deposits. A filter can be applied to " +
"only show deposits in a specific state.",
Description: `
`,
Flags: []cli.Flag{
cli.StringFlag{
&cli.StringFlag{
Name: "filter",
Usage: "specify a filter to only display deposits in " +
"the specified state. Leaving out the filter " +
@ -248,20 +245,19 @@ var listDepositsCommand = cli.Command{
Action: listDeposits,
}
func listDeposits(ctx *cli.Context) error {
ctxb := context.Background()
if ctx.NArg() > 0 {
return cli.ShowCommandHelp(ctx, "listdeposits")
func listDeposits(ctx context.Context, cmd *cli.Command) error {
if cmd.NArg() > 0 {
return showCommandHelp(ctx, cmd)
}
client, cleanup, err := getClient(ctx)
client, cleanup, err := getClient(ctx, cmd)
if err != nil {
return err
}
defer cleanup()
var filterState looprpc.DepositState
switch ctx.String("filter") {
switch cmd.String("filter") {
case "":
// If no filter is specified, we'll default to showing all.
@ -300,7 +296,7 @@ func listDeposits(ctx *cli.Context) error {
}
resp, err := client.ListStaticAddressDeposits(
ctxb, &looprpc.ListStaticAddressDepositsRequest{
ctx, &looprpc.ListStaticAddressDepositsRequest{
StateFilter: filterState,
},
)
@ -313,7 +309,7 @@ func listDeposits(ctx *cli.Context) error {
return nil
}
var listWithdrawalsCommand = cli.Command{
var listWithdrawalsCommand = &cli.Command{
Name: "listwithdrawals",
Usage: "Display a summary of past withdrawals.",
Description: `
@ -321,20 +317,19 @@ var listWithdrawalsCommand = cli.Command{
Action: listWithdrawals,
}
func listWithdrawals(ctx *cli.Context) error {
ctxb := context.Background()
if ctx.NArg() > 0 {
return cli.ShowCommandHelp(ctx, "withdrawals")
func listWithdrawals(ctx context.Context, cmd *cli.Command) error {
if cmd.NArg() > 0 {
return showCommandHelp(ctx, cmd)
}
client, cleanup, err := getClient(ctx)
client, cleanup, err := getClient(ctx, cmd)
if err != nil {
return err
}
defer cleanup()
resp, err := client.ListStaticAddressWithdrawals(
ctxb, &looprpc.ListStaticAddressWithdrawalRequest{},
ctx, &looprpc.ListStaticAddressWithdrawalRequest{},
)
if err != nil {
return err
@ -345,7 +340,7 @@ func listWithdrawals(ctx *cli.Context) error {
return nil
}
var listStaticAddressSwapsCommand = cli.Command{
var listStaticAddressSwapsCommand = &cli.Command{
Name: "listswaps",
Usage: "Shows a list of finalized static address swaps.",
Description: `
@ -353,20 +348,19 @@ var listStaticAddressSwapsCommand = cli.Command{
Action: listStaticAddressSwaps,
}
func listStaticAddressSwaps(ctx *cli.Context) error {
ctxb := context.Background()
if ctx.NArg() > 0 {
return cli.ShowCommandHelp(ctx, "listswaps")
func listStaticAddressSwaps(ctx context.Context, cmd *cli.Command) error {
if cmd.NArg() > 0 {
return showCommandHelp(ctx, cmd)
}
client, cleanup, err := getClient(ctx)
client, cleanup, err := getClient(ctx, cmd)
if err != nil {
return err
}
defer cleanup()
resp, err := client.ListStaticAddressSwaps(
ctxb, &looprpc.ListStaticAddressSwapsRequest{},
ctx, &looprpc.ListStaticAddressSwapsRequest{},
)
if err != nil {
return err
@ -377,10 +371,10 @@ func listStaticAddressSwaps(ctx *cli.Context) error {
return nil
}
var summaryCommand = cli.Command{
Name: "summary",
ShortName: "s",
Usage: "Display a summary of static address related information.",
var summaryCommand = &cli.Command{
Name: "summary",
Aliases: []string{"s"},
Usage: "Display a summary of static address related information.",
Description: `
Displays various static address related information about deposits,
withdrawals and swaps.
@ -388,20 +382,19 @@ var summaryCommand = cli.Command{
Action: summary,
}
func summary(ctx *cli.Context) error {
ctxb := context.Background()
if ctx.NArg() > 0 {
return cli.ShowCommandHelp(ctx, "summary")
func summary(ctx context.Context, cmd *cli.Command) error {
if cmd.NArg() > 0 {
return showCommandHelp(ctx, cmd)
}
client, cleanup, err := getClient(ctx)
client, cleanup, err := getClient(ctx, cmd)
if err != nil {
return err
}
defer cleanup()
resp, err := client.GetStaticAddressSummary(
ctxb, &looprpc.StaticAddressSummaryRequest{},
ctx, &looprpc.StaticAddressSummaryRequest{},
)
if err != nil {
return err
@ -452,9 +445,10 @@ func NewProtoOutPoint(op string) (*looprpc.OutPoint, error) {
}, nil
}
var staticAddressLoopInCommand = cli.Command{
Name: "in",
Usage: "Loop in funds from static address deposits.",
var staticAddressLoopInCommand = &cli.Command{
Name: "in",
Usage: "Loop in funds from static address deposits.",
ArgsUsage: "[amt] [--all | --utxo xxx:xx]",
Description: `
Requests a loop-in swap based on static address deposits. After the
creation of a static address funds can be sent to it. Once the funds are
@ -462,30 +456,30 @@ var staticAddressLoopInCommand = cli.Command{
funds are not needed they can we withdrawn back to the local lnd wallet.
`,
Flags: []cli.Flag{
cli.StringSliceFlag{
&cli.StringSliceFlag{
Name: "utxo",
Usage: "specify the utxos of deposits as " +
"outpoints(tx:idx) that should be looped in.",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "all",
Usage: "loop in all static address deposits.",
},
cli.DurationFlag{
&cli.DurationFlag{
Name: "payment_timeout",
Usage: "the maximum time in seconds that the server " +
"is allowed to take for the swap payment. " +
"The client can retry the swap with adjusted " +
"parameters after the payment timed out.",
},
cli.Uint64Flag{
&cli.Uint64Flag{
Name: "amount",
Usage: "the number of satoshis that should be " +
"swapped from the selected deposits. If there" +
"is change it is sent back to the static " +
"address.",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "fast",
Usage: "Usage: complete the swap faster by paying a " +
"higher fee, so the change output is " +
@ -501,40 +495,39 @@ var staticAddressLoopInCommand = cli.Command{
Action: staticAddressLoopIn,
}
func staticAddressLoopIn(ctx *cli.Context) error {
if ctx.NumFlags() == 0 && ctx.NArg() == 0 {
return cli.ShowCommandHelp(ctx, "in")
func staticAddressLoopIn(ctx context.Context, cmd *cli.Command) error {
if cmd.NumFlags() == 0 && cmd.NArg() == 0 {
return showCommandHelp(ctx, cmd)
}
var selectedAmount int64
switch {
case ctx.NArg() == 1:
amt, err := parseAmt(ctx.Args().Get(0))
case cmd.NArg() == 1:
amt, err := parseAmt(cmd.Args().Get(0))
if err != nil {
return err
}
selectedAmount = int64(amt)
case ctx.NArg() > 1:
case cmd.NArg() > 1:
return fmt.Errorf("only a single positional argument is " +
"allowed")
default:
selectedAmount = ctx.Int64("amount")
selectedAmount = cmd.Int64("amount")
}
client, cleanup, err := getClient(ctx)
client, cleanup, err := getClient(ctx, cmd)
if err != nil {
return err
}
defer cleanup()
var (
ctxb = context.Background()
isAllSelected = ctx.IsSet("all")
isUtxoSelected = ctx.IsSet("utxo")
isAllSelected = cmd.IsSet("all")
isUtxoSelected = cmd.IsSet("utxo")
autoSelectDepositsForQuote bool
label = ctx.String(labelFlag.Name)
label = cmd.String(labelFlag.Name)
hints []*swapserverrpc.RouteHint
lastHop []byte
paymentTimeoutSeconds = uint32(loopin.DefaultPaymentTimeoutSeconds)
@ -547,14 +540,14 @@ func staticAddressLoopIn(ctx *cli.Context) error {
// Private and route hints are mutually exclusive as setting private
// means we retrieve our own route hints from the connected node.
hints, err = validateRouteHints(ctx)
hints, err = validateRouteHints(cmd)
if err != nil {
return err
}
if ctx.IsSet(lastHopFlag.Name) {
if cmd.IsSet(lastHopFlag.Name) {
lastHopVertex, err := route.NewVertexFromStr(
ctx.String(lastHopFlag.Name),
cmd.String(lastHopFlag.Name),
)
if err != nil {
return err
@ -565,7 +558,7 @@ func staticAddressLoopIn(ctx *cli.Context) error {
// Get the amount we need to quote for.
depositList, err := client.ListStaticAddressDeposits(
ctxb, &looprpc.ListStaticAddressDepositsRequest{
ctx, &looprpc.ListStaticAddressDepositsRequest{
StateFilter: looprpc.DepositState_DEPOSITED,
},
)
@ -592,7 +585,7 @@ func staticAddressLoopIn(ctx *cli.Context) error {
depositOutpoints = depositsToOutpoints(allDeposits)
case isUtxoSelected:
depositOutpoints = ctx.StringSlice("utxo")
depositOutpoints = cmd.StringSlice("utxo")
case selectedAmount > 0:
// If only an amount is selected, we will trigger coin
@ -614,27 +607,27 @@ func staticAddressLoopIn(ctx *cli.Context) error {
Amt: selectedAmount,
LoopInRouteHints: hints,
LoopInLastHop: lastHop,
Private: ctx.Bool(privateFlag.Name),
Private: cmd.Bool(privateFlag.Name),
DepositOutpoints: depositOutpoints,
AutoSelectDeposits: autoSelectDepositsForQuote,
Fast: ctx.Bool("fast"),
Fast: cmd.Bool("fast"),
}
quote, err := client.GetLoopInQuote(ctxb, quoteReq)
quote, err := client.GetLoopInQuote(ctx, quoteReq)
if err != nil {
return err
}
limits := getInLimits(quote)
if !(ctx.Bool("force") || ctx.Bool("f")) {
err = displayInDetails(quoteReq, quote, ctx.Bool("verbose"))
if !(cmd.Bool("force") || cmd.Bool("f")) {
err = displayInDetails(quoteReq, quote, cmd.Bool("verbose"))
if err != nil {
return err
}
}
if ctx.IsSet("payment_timeout") {
paymentTimeoutSeconds = uint32(ctx.Duration("payment_timeout").Seconds())
if cmd.IsSet("payment_timeout") {
paymentTimeoutSeconds = uint32(cmd.Duration("payment_timeout").Seconds())
}
req := &looprpc.StaticAddressLoopInRequest{
@ -645,12 +638,12 @@ func staticAddressLoopIn(ctx *cli.Context) error {
Label: label,
Initiator: defaultInitiator,
RouteHints: hints,
Private: ctx.Bool("private"),
Private: cmd.Bool("private"),
PaymentTimeoutSeconds: paymentTimeoutSeconds,
Fast: ctx.Bool("fast"),
Fast: cmd.Bool("fast"),
}
resp, err := client.StaticAddressLoopIn(ctxb, req)
resp, err := client.StaticAddressLoopIn(ctx, req)
if err != nil {
return err
}

View file

@ -10,36 +10,36 @@ import (
"github.com/lightninglabs/loop/looprpc"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/urfave/cli"
"github.com/urfave/cli/v3"
)
var listSwapsCommand = cli.Command{
var listSwapsCommand = &cli.Command{
Name: "listswaps",
Usage: "list all swaps in the local database",
Description: "Allows the user to get a list of all swaps that are " +
"currently stored in the database",
Action: listSwaps,
Flags: []cli.Flag{
cli.BoolFlag{
&cli.BoolFlag{
Name: "loop_out_only",
Usage: "only list swaps that are loop out swaps",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "loop_in_only",
Usage: "only list swaps that are loop in swaps",
},
cli.BoolFlag{
&cli.BoolFlag{
Name: "pending_only",
Usage: "only list pending swaps",
},
labelFlag,
channelFlag,
lastHopFlag,
cli.Uint64Flag{
&cli.Uint64Flag{
Name: "max_swaps",
Usage: "Max number of swaps to return after filtering",
},
cli.Int64Flag{
&cli.Int64Flag{
Name: "start_time_ns",
Usage: "Unix timestamp in nanoseconds to select swaps initiated " +
"after this time",
@ -47,14 +47,14 @@ var listSwapsCommand = cli.Command{
},
}
func listSwaps(ctx *cli.Context) error {
client, cleanup, err := getClient(ctx)
func listSwaps(ctx context.Context, cmd *cli.Command) error {
client, cleanup, err := getClient(ctx, cmd)
if err != nil {
return err
}
defer cleanup()
if ctx.Bool("loop_out_only") && ctx.Bool("loop_in_only") {
if cmd.Bool("loop_out_only") && cmd.Bool("loop_in_only") {
return fmt.Errorf("only one of loop_out_only and loop_in_only " +
"can be set")
}
@ -63,21 +63,21 @@ func listSwaps(ctx *cli.Context) error {
// Set the swap type filter.
switch {
case ctx.Bool("loop_out_only"):
case cmd.Bool("loop_out_only"):
filter.SwapType = looprpc.ListSwapsFilter_LOOP_OUT
case ctx.Bool("loop_in_only"):
case cmd.Bool("loop_in_only"):
filter.SwapType = looprpc.ListSwapsFilter_LOOP_IN
}
// Set the pending only filter.
filter.PendingOnly = ctx.Bool("pending_only")
filter.PendingOnly = cmd.Bool("pending_only")
// 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(channelFlag.Name) {
chanStrings := strings.Split(ctx.String(channelFlag.Name), ",")
if cmd.IsSet(channelFlag.Name) {
chanStrings := strings.Split(cmd.String(channelFlag.Name), ",")
for _, chanString := range chanStrings {
chanID, err := strconv.ParseUint(chanString, 10, 64)
if err != nil {
@ -91,9 +91,9 @@ func listSwaps(ctx *cli.Context) error {
// Parse last hop.
var lastHop []byte
if ctx.IsSet(lastHopFlag.Name) {
if cmd.IsSet(lastHopFlag.Name) {
lastHopVertex, err := route.NewVertexFromStr(
ctx.String(lastHopFlag.Name),
cmd.String(lastHopFlag.Name),
)
if err != nil {
return err
@ -104,19 +104,19 @@ func listSwaps(ctx *cli.Context) error {
}
// Parse label.
if ctx.IsSet(labelFlag.Name) {
filter.Label = ctx.String(labelFlag.Name)
if cmd.IsSet(labelFlag.Name) {
filter.Label = cmd.String(labelFlag.Name)
}
// Parse start timestamp if set.
if ctx.IsSet("start_time_ns") {
filter.StartTimestampNs = ctx.Int64("start_time_ns")
if cmd.IsSet("start_time_ns") {
filter.StartTimestampNs = cmd.Int64("start_time_ns")
}
resp, err := client.ListSwaps(
context.Background(), &looprpc.ListSwapsRequest{
ctx, &looprpc.ListSwapsRequest{
ListSwapFilter: filter,
MaxSwaps: ctx.Uint64("max_swaps"),
MaxSwaps: cmd.Uint64("max_swaps"),
},
)
if err != nil {
@ -127,14 +127,14 @@ func listSwaps(ctx *cli.Context) error {
return nil
}
var swapInfoCommand = cli.Command{
var swapInfoCommand = &cli.Command{
Name: "swapinfo",
Usage: "show the status of a swap",
ArgsUsage: "id",
Description: "Allows the user to get the status of a single swap " +
"currently stored in the database",
Flags: []cli.Flag{
cli.Uint64Flag{
&cli.Uint64Flag{
Name: "id",
Usage: "the ID of the swap",
},
@ -142,19 +142,18 @@ var swapInfoCommand = cli.Command{
Action: swapInfo,
}
func swapInfo(ctx *cli.Context) error {
args := ctx.Args()
func swapInfo(ctx context.Context, cmd *cli.Command) error {
args := cmd.Args()
var id string
switch {
case ctx.IsSet("id"):
id = ctx.String("id")
case ctx.NArg() > 0:
id = args[0]
args = args.Tail() // nolint:wastedassign
case cmd.IsSet("id"):
id = cmd.String("id")
case cmd.NArg() > 0:
id = args.First()
default:
// Show command help if no arguments and flags were provided.
return cli.ShowCommandHelp(ctx, "swapinfo")
return showCommandHelp(ctx, cmd)
}
if len(id) != hex.EncodedLen(lntypes.HashSize) {
@ -165,14 +164,14 @@ func swapInfo(ctx *cli.Context) error {
return fmt.Errorf("cannot hex decode id: %v", err)
}
client, cleanup, err := getClient(ctx)
client, cleanup, err := getClient(ctx, cmd)
if err != nil {
return err
}
defer cleanup()
resp, err := client.SwapInfo(
context.Background(), &looprpc.SwapInfoRequest{Id: idBytes},
ctx, &looprpc.SwapInfoRequest{Id: idBytes},
)
if err != nil {
return err
@ -182,7 +181,7 @@ func swapInfo(ctx *cli.Context) error {
return nil
}
var abandonSwapCommand = cli.Command{
var abandonSwapCommand = &cli.Command{
Name: "abandonswap",
Usage: "abandon a swap with a given swap hash",
Description: "This command overrides the database and abandons a " +
@ -193,7 +192,7 @@ var abandonSwapCommand = cli.Command{
"no funds are locked by the swap.",
ArgsUsage: "ID",
Flags: []cli.Flag{
cli.BoolFlag{
&cli.BoolFlag{
Name: "i_know_what_i_am_doing",
Usage: "Specify this flag if you made sure that you " +
"read and understood the following " +
@ -203,21 +202,20 @@ var abandonSwapCommand = cli.Command{
Action: abandonSwap,
}
func abandonSwap(ctx *cli.Context) error {
args := ctx.Args()
func abandonSwap(ctx context.Context, cmd *cli.Command) error {
args := cmd.Args()
var id string
switch {
case ctx.IsSet("id"):
id = ctx.String("id")
case cmd.IsSet("id"):
id = cmd.String("id")
case ctx.NArg() > 0:
id = args[0]
args = args.Tail() // nolint:wastedassign
case cmd.NArg() > 0:
id = args.First()
default:
// Show command help if no arguments and flags were provided.
return cli.ShowCommandHelp(ctx, "abandonswap")
return showCommandHelp(ctx, cmd)
}
if len(id) != hex.EncodedLen(lntypes.HashSize) {
@ -228,20 +226,20 @@ func abandonSwap(ctx *cli.Context) error {
return fmt.Errorf("cannot hex decode id: %v", err)
}
client, cleanup, err := getClient(ctx)
client, cleanup, err := getClient(ctx, cmd)
if err != nil {
return err
}
defer cleanup()
if !ctx.Bool("i_know_what_i_am_doing") {
return cli.ShowCommandHelp(ctx, "abandonswap")
if !cmd.Bool("i_know_what_i_am_doing") {
return showCommandHelp(ctx, cmd)
}
resp, err := client.AbandonSwap(
context.Background(), &looprpc.AbandonSwapRequest{
ctx, &looprpc.AbandonSwapRequest{
Id: idBytes,
IKnowWhatIAmDoing: ctx.Bool("i_know_what_i_am_doing"),
IKnowWhatIAmDoing: cmd.Bool("i_know_what_i_am_doing"),
},
)
if err != nil {

View file

@ -6,17 +6,17 @@ import (
"github.com/btcsuite/btcd/btcutil"
"github.com/lightninglabs/loop/looprpc"
"github.com/urfave/cli"
"github.com/urfave/cli/v3"
)
var termsCommand = cli.Command{
var termsCommand = &cli.Command{
Name: "terms",
Usage: "Display the current swap terms imposed by the server.",
Action: terms,
}
func terms(ctx *cli.Context) error {
client, cleanup, err := getClient(ctx)
func terms(ctx context.Context, cmd *cli.Command) error {
client, cleanup, err := getClient(ctx, cmd)
if err != nil {
return err
}
@ -31,7 +31,7 @@ func terms(ctx *cli.Context) error {
fmt.Println("Loop Out")
fmt.Println("--------")
req := &looprpc.TermsRequest{}
loopOutTerms, err := client.LoopOutTerms(context.Background(), req)
loopOutTerms, err := client.LoopOutTerms(ctx, req)
if err != nil {
fmt.Println(err)
} else {
@ -49,7 +49,7 @@ func terms(ctx *cli.Context) error {
fmt.Println("Loop In")
fmt.Println("------")
loopInTerms, err := client.GetLoopInTerms(
context.Background(), &looprpc.TermsRequest{},
ctx, &looprpc.TermsRequest{},
)
if err != nil {
fmt.Println(err)

View file

@ -1,28 +1,41 @@
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/lightninglabs/loop/swapserverrpc"
"github.com/urfave/cli"
"github.com/urfave/cli/v3"
)
// showCommandHelp prints help for the current command by delegating to the
// parent command when available. This ensures help output renders even when
// invoked from inside a subcommand's action.
func showCommandHelp(ctx context.Context, cmd *cli.Command) error {
lineage := cmd.Lineage()
if len(lineage) > 1 {
parent := lineage[1]
return cli.ShowCommandHelp(ctx, parent, cmd.Name)
}
return cli.ShowCommandHelp(ctx, cmd, cmd.Name)
}
// validateRouteHints ensures that the Private flag isn't set along with
// the RouteHints flag. We don't allow both options to be set as these options
// are alternatives to each other. Private autogenerates hopHints while
// RouteHints are manually passed.
func validateRouteHints(ctx *cli.Context) ([]*swapserverrpc.RouteHint, error) {
func validateRouteHints(cmd *cli.Command) ([]*swapserverrpc.RouteHint, error) {
var hints []*swapserverrpc.RouteHint
if ctx.IsSet(routeHintsFlag.Name) {
if ctx.IsSet(privateFlag.Name) {
if cmd.IsSet(routeHintsFlag.Name) {
if cmd.IsSet(privateFlag.Name) {
return nil, fmt.Errorf(
"private and route_hints both set",
)
}
jsonHints := ctx.StringSlice(routeHintsFlag.Name)
jsonHints := cmd.StringSlice(routeHintsFlag.Name)
hints := make([]*swapserverrpc.RouteHint, len(jsonHints))
for i, jsonHint := range jsonHints {

4
go.mod
View file

@ -34,7 +34,7 @@ require (
github.com/lightningnetwork/lnd/tor v1.1.6
github.com/ory/dockertest/v3 v3.10.0
github.com/stretchr/testify v1.10.0
github.com/urfave/cli v1.22.14
github.com/urfave/cli/v3 v3.4.1
go.etcd.io/bbolt v1.3.11
golang.org/x/sync v0.12.0
google.golang.org/grpc v1.64.1
@ -72,7 +72,6 @@ require (
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect
github.com/decred/dcrd/lru v1.1.2 // indirect
github.com/docker/cli v28.0.1+incompatible // indirect
@ -150,7 +149,6 @@ require (
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rogpeppe/fastuuid v1.2.0 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/soheilhy/cmux v0.1.5 // indirect
github.com/spf13/pflag v1.0.6 // indirect

10
go.sum
View file

@ -600,7 +600,6 @@ git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3p
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs=
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
@ -737,8 +736,6 @@ github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pq
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
@ -1266,8 +1263,6 @@ github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
@ -1306,7 +1301,6 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
@ -1315,8 +1309,8 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 h1:uruHq4
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tv42/zbase32 v0.0.0-20160707012821-501572607d02 h1:tcJ6OjwOMvExLlzrAVZute09ocAGa7KqOON60++Gz4E=
github.com/tv42/zbase32 v0.0.0-20160707012821-501572607d02/go.mod h1:tHlrkM198S068ZqfrO6S8HsoJq2bF3ETfTL+kt4tInY=
github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk=
github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA=
github.com/urfave/cli/v3 v3.4.1 h1:1M9UOCy5bLmGnuu1yn3t3CB4rG79Rtoxuv1sPhnm6qM=
github.com/urfave/cli/v3 v3.4.1/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=