@@ -10,6 +10,8 @@ import (
1010
1111 "github.com/btcsuite/btcd/chaincfg/chainhash"
1212 "github.com/lightninglabs/loop/looprpc"
13+ "github.com/lightninglabs/loop/swapserverrpc"
14+ "github.com/lightningnetwork/lnd/routing/route"
1315 "github.com/urfave/cli"
1416)
1517
@@ -23,6 +25,7 @@ var staticAddressCommands = cli.Command{
2325 listUnspentCommand ,
2426 withdrawalCommand ,
2527 summaryCommand ,
28+ staticQuoteCommand ,
2629 },
2730}
2831
@@ -297,3 +300,138 @@ func NewProtoOutPoint(op string) (*looprpc.OutPoint, error) {
297300 OutputIndex : uint32 (outputIndex ),
298301 }, nil
299302}
303+
304+ var staticQuoteCommand = cli.Command {
305+ Name : "quote" ,
306+ ShortName : "q" ,
307+ Usage : "quote..." ,
308+ Description : `
309+ Allows to determine the cost of a swap up front.
310+ ` ,
311+ Flags : []cli.Flag {
312+ cli.StringSliceFlag {
313+ Name : "utxo" ,
314+ Usage : "specify utxos as outpoints(tx:idx) which will" +
315+ "be quoted for." ,
316+ },
317+ cli.BoolFlag {
318+ Name : "all" ,
319+ Usage : "quotes for all static address deposits." ,
320+ },
321+ cli.StringFlag {
322+ Name : lastHopFlag .Name ,
323+ Usage : "the pubkey of the last hop to use for the " +
324+ "quote" ,
325+ },
326+ privateFlag ,
327+ routeHintsFlag ,
328+ },
329+ Action : staticAddressQuote ,
330+ }
331+
332+ func staticAddressQuote (ctx * cli.Context ) error {
333+ if ctx .NArg () > 0 {
334+ return cli .ShowCommandHelp (ctx , "new" )
335+ }
336+
337+ client , cleanup , err := getClient (ctx )
338+ if err != nil {
339+ return err
340+ }
341+ defer cleanup ()
342+
343+ var (
344+ ctxb = context .Background ()
345+ isAllSelected = ctx .IsSet ("all" )
346+ isUtxoSelected = ctx .IsSet ("utxo" )
347+ hints []* swapserverrpc.RouteHint
348+ lastHop []byte
349+ )
350+
351+ // Private and route hints are mutually exclusive as setting private
352+ // means we retrieve our own route hints from the connected node.
353+ hints , err = validateRouteHints (ctx )
354+ if err != nil {
355+ return err
356+ }
357+
358+ if ctx .IsSet (lastHopFlag .Name ) {
359+ lastHopVertex , err := route .NewVertexFromStr (
360+ ctx .String (lastHopFlag .Name ),
361+ )
362+ if err != nil {
363+ return err
364+ }
365+
366+ lastHop = lastHopVertex [:]
367+ }
368+
369+ summaryResp , err := client .GetStaticAddressSummary (
370+ ctxb , & looprpc.StaticAddressSummaryRequest {},
371+ )
372+ if err != nil {
373+ return err
374+ }
375+
376+ var quoteAmount int64
377+ var numDeposits uint32
378+ switch {
379+ case isAllSelected == isUtxoSelected :
380+ return errors .New ("must select either all or some utxos" )
381+
382+ case isAllSelected :
383+ numDeposits = summaryResp .TotalNumDeposits
384+ quoteAmount = summaryResp .ValueDeposited
385+
386+ case isUtxoSelected :
387+ utxos := ctx .StringSlice ("utxo" )
388+ numDeposits = uint32 (len (utxos ))
389+ quoteAmount , err = sumOutpointValues (
390+ utxos , summaryResp .FilteredDeposits ,
391+ )
392+ if err != nil {
393+ return err
394+ }
395+
396+ default :
397+ return fmt .Errorf ("unknown quote request" )
398+ }
399+
400+ resp , err := client .GetLoopInQuote (
401+ ctxb , & looprpc.QuoteRequest {
402+ Amt : quoteAmount ,
403+ LoopInRouteHints : hints ,
404+ LoopInLastHop : lastHop ,
405+ Private : ctx .Bool (privateFlag .Name ),
406+ NumDeposits : numDeposits ,
407+ },
408+ )
409+ if err != nil {
410+ return err
411+ }
412+
413+ printRespJSON (resp )
414+
415+ return nil
416+ }
417+
418+ func sumOutpointValues (utxos []string , deposits []* looprpc.Deposit ) (int64 ,
419+ error ) {
420+
421+ var sum int64
422+ for _ , utxo := range utxos {
423+ found := false
424+ for _ , deposit := range deposits {
425+ if deposit .Outpoint == utxo {
426+ sum += deposit .Value
427+ found = true
428+ }
429+ }
430+ if ! found {
431+ return 0 , fmt .Errorf ("utxo %v not found in deposits" ,
432+ utxo )
433+ }
434+ }
435+
436+ return sum , nil
437+ }
0 commit comments