7979 // quote call as the miner fee if the fee estimation in lnd's wallet
8080 // failed because of insufficient funds.
8181 MinerFeeEstimationFailed btcutil.Amount = - 1
82+
83+ // defaultRFQExpiry is the default expiry time for RFQs.
84+ defaultRFQExpiry = 5 * time .Minute
85+
86+ // defaultRFQMaxLimitMultiplier is the default maximum fee multiplier for
87+ // RFQs.
88+ defaultRFQMaxLimitMultiplier = 1.1
8289)
8390
8491// Client performs the client side part of swaps. This interface exists to be
@@ -506,9 +513,30 @@ func (s *Client) resumeSwaps(ctx context.Context,
506513func (s * Client ) LoopOut (globalCtx context.Context ,
507514 request * OutRequest ) (* LoopOutSwapInfo , error ) {
508515
509- log .Infof ("LoopOut %v to %v (channels: %v)" ,
510- request .Amount , request .DestAddr , request .OutgoingChanSet ,
511- )
516+ if request .AssetId != nil {
517+ if request .AssetPrepayRfqId == nil ||
518+ request .AssetSwapRfqId == nil {
519+
520+ return nil , errors .New ("asset prepay and swap rfq ids " +
521+ "must be set when using an asset id" )
522+ }
523+
524+ // Verify that if we have an asset id set, we have a valid asset
525+ // client to use.
526+ if s .assetClient == nil {
527+ return nil , errors .New ("asset client must be set " +
528+ "when using an asset id" )
529+ }
530+
531+ log .Infof ("LoopOut %v sats to %v with asset %x" ,
532+ request .Amount , request .DestAddr , request .AssetId ,
533+ )
534+ } else {
535+ log .Infof ("LoopOut %v to %v (channels: %v)" ,
536+ request .Amount , request .DestAddr ,
537+ request .OutgoingChanSet ,
538+ )
539+ }
512540
513541 if err := s .waitForInitialized (globalCtx ); err != nil {
514542 return nil , err
@@ -529,7 +557,10 @@ func (s *Client) LoopOut(globalCtx context.Context,
529557 }
530558
531559 // Create a new swap object for this swap.
532- swapCfg := newSwapConfig (s .lndServices , s .Store , s .Server , s .assetClient )
560+ swapCfg := newSwapConfig (
561+ s .lndServices , s .Store , s .Server , s .assetClient ,
562+ )
563+
533564 initResult , err := newLoopOutSwap (
534565 globalCtx , swapCfg , initiationHeight , request ,
535566 )
@@ -574,6 +605,19 @@ func (s *Client) getExpiry(height int32, terms *LoopOutTerms,
574605func (s * Client ) LoopOutQuote (ctx context.Context ,
575606 request * LoopOutQuoteRequest ) (* LoopOutQuote , error ) {
576607
608+ if request .AssetRFQRequest != nil {
609+ rfqReq := request .AssetRFQRequest
610+ if rfqReq .AssetId != nil && rfqReq .AssetEdgeNode == nil {
611+ return nil , errors .New ("asset edge node must be set " +
612+ "when using an asset id" )
613+ }
614+
615+ if rfqReq .AssetEdgeNode != nil && rfqReq .AssetId == nil {
616+ return nil , errors .New ("asset id must be set " +
617+ "when using an asset edge node" )
618+ }
619+ }
620+
577621 terms , err := s .Server .GetLoopOutTerms (ctx , request .Initiator )
578622 if err != nil {
579623 return nil , err
@@ -608,12 +652,67 @@ func (s *Client) LoopOutQuote(ctx context.Context,
608652 return nil , err
609653 }
610654
611- return & LoopOutQuote {
655+ loopOutQuote := & LoopOutQuote {
612656 SwapFee : quote .SwapFee ,
613657 MinerFee : minerFee ,
614658 PrepayAmount : quote .PrepayAmount ,
615659 SwapPaymentDest : quote .SwapPaymentDest ,
616- }, nil
660+ }
661+
662+ // If we use an Asset we'll rfq to get the asset amounts to use for
663+ // the swap.
664+ if request .AssetRFQRequest != nil {
665+ rfqReq := request .AssetRFQRequest
666+ if rfqReq .Expiry == 0 {
667+ rfqReq .Expiry = time .Now ().Add (defaultRFQExpiry ).Unix ()
668+ }
669+
670+ if rfqReq .MaxLimitMultiplier == 0 {
671+ rfqReq .MaxLimitMultiplier = defaultRFQMaxLimitMultiplier
672+ }
673+
674+ // First we'll get the prepay rfq.
675+ prepayRfq , err := s .assetClient .GetRfqForAsset (
676+ ctx , quote .PrepayAmount , rfqReq .AssetId ,
677+ rfqReq .AssetEdgeNode , rfqReq .Expiry ,
678+ rfqReq .MaxLimitMultiplier ,
679+ )
680+ if err != nil {
681+ return nil , err
682+ }
683+
684+ // The actual invoice swap amount is the requested amount plus
685+ // the swap fee minus the prepay amount.
686+ invoiceAmt := request .Amount + quote .SwapFee -
687+ quote .PrepayAmount
688+
689+ swapRfq , err := s .assetClient .GetRfqForAsset (
690+ ctx , invoiceAmt , rfqReq .AssetId ,
691+ rfqReq .AssetEdgeNode , rfqReq .Expiry ,
692+ rfqReq .MaxLimitMultiplier ,
693+ )
694+ if err != nil {
695+ return nil , err
696+ }
697+
698+ // We'll also want the asset name to verify for the client.
699+ assetName , err := s .assetClient .GetAssetName (
700+ ctx , rfqReq .AssetId ,
701+ )
702+ if err != nil {
703+ return nil , err
704+ }
705+
706+ loopOutQuote .LoopOutRfq = & LoopOutRfq {
707+ PrepayRfqId : prepayRfq .Id ,
708+ PrepayAssetAmt : btcutil .Amount (prepayRfq .AssetAmount ),
709+ SwapRfqId : swapRfq .Id ,
710+ SwapAssetAmt : btcutil .Amount (swapRfq .AssetAmount ),
711+ AssetName : assetName ,
712+ }
713+ }
714+
715+ return loopOutQuote , nil
617716}
618717
619718// getLoopOutSweepFee is a helper method to estimate the loop out htlc sweep
0 commit comments