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.2
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,14 @@ 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 ("both asset edge node and " +
612+ "asset id must be set" )
613+ }
614+ }
615+
577616 terms , err := s .Server .GetLoopOutTerms (ctx , request .Initiator )
578617 if err != nil {
579618 return nil , err
@@ -608,12 +647,67 @@ func (s *Client) LoopOutQuote(ctx context.Context,
608647 return nil , err
609648 }
610649
611- return & LoopOutQuote {
650+ loopOutQuote := & LoopOutQuote {
612651 SwapFee : quote .SwapFee ,
613652 MinerFee : minerFee ,
614653 PrepayAmount : quote .PrepayAmount ,
615654 SwapPaymentDest : quote .SwapPaymentDest ,
616- }, nil
655+ }
656+
657+ // If we use an Asset we'll rfq to get the asset amounts to use for
658+ // the swap.
659+ if request .AssetRFQRequest != nil {
660+ rfqReq := request .AssetRFQRequest
661+ if rfqReq .Expiry == 0 {
662+ rfqReq .Expiry = time .Now ().Add (defaultRFQExpiry ).Unix ()
663+ }
664+
665+ if rfqReq .MaxLimitMultiplier == 0 {
666+ rfqReq .MaxLimitMultiplier = defaultRFQMaxLimitMultiplier
667+ }
668+
669+ // First we'll get the prepay rfq.
670+ prepayRfq , err := s .assetClient .GetRfqForAsset (
671+ ctx , quote .PrepayAmount , rfqReq .AssetId ,
672+ rfqReq .AssetEdgeNode , rfqReq .Expiry ,
673+ rfqReq .MaxLimitMultiplier ,
674+ )
675+ if err != nil {
676+ return nil , err
677+ }
678+
679+ // The actual invoice swap amount is the requested amount plus
680+ // the swap fee minus the prepay amount.
681+ invoiceAmt := request .Amount + quote .SwapFee -
682+ quote .PrepayAmount
683+
684+ swapRfq , err := s .assetClient .GetRfqForAsset (
685+ ctx , invoiceAmt , rfqReq .AssetId ,
686+ rfqReq .AssetEdgeNode , rfqReq .Expiry ,
687+ rfqReq .MaxLimitMultiplier ,
688+ )
689+ if err != nil {
690+ return nil , err
691+ }
692+
693+ // We'll also want the asset name to verify for the client.
694+ assetName , err := s .assetClient .GetAssetName (
695+ ctx , rfqReq .AssetId ,
696+ )
697+ if err != nil {
698+ return nil , err
699+ }
700+
701+ loopOutQuote .LoopOutRfq = & LoopOutRfq {
702+ PrepayRfqId : prepayRfq .Id ,
703+ PrepayAssetAmt : prepayRfq .AssetAmount ,
704+ SwapRfqId : swapRfq .Id ,
705+ SwapAssetAmt : swapRfq .AssetAmount ,
706+ AssetName : assetName ,
707+ }
708+ }
709+
710+ return loopOutQuote , nil
617711}
618712
619713// getLoopOutSweepFee is a helper method to estimate the loop out htlc sweep
0 commit comments