@@ -7822,3 +7822,146 @@ func (r *rpcServer) getInboundPolicy(ctx context.Context, chanID uint64,
78227822
78237823 return policy , nil
78247824}
7825+
7826+ // assetInvoiceAmt calculates the amount of asset units to pay for an invoice
7827+ // which is expressed in sats.
7828+ func (r * rpcServer ) assetInvoiceAmt (ctx context.Context ,
7829+ targetAsset asset.Specifier ,
7830+ invoiceAmt lnwire.MilliSatoshi ) (uint64 , error ) {
7831+
7832+ oracle := r .cfg .PriceOracle
7833+
7834+ oracleResp , err := oracle .QueryAskPrice (
7835+ ctx , targetAsset , fn .None [uint64 ](), fn .Some (invoiceAmt ),
7836+ fn .None [rfqmsg.AssetRate ](),
7837+ )
7838+ if err != nil {
7839+ return 0 , fmt .Errorf ("error querying ask price: %w" , err )
7840+ }
7841+ if oracleResp .Err != nil {
7842+ return 0 , fmt .Errorf ("error querying ask price: %w" ,
7843+ oracleResp .Err )
7844+ }
7845+
7846+ assetRate := oracleResp .AssetRate .Rate
7847+
7848+ numAssetUnits := rfqmath .MilliSatoshiToUnits (
7849+ invoiceAmt , assetRate ,
7850+ ).ScaleTo (0 )
7851+
7852+ return numAssetUnits .ToUint64 (), nil
7853+ }
7854+
7855+ // DecodeAssetPayReq decodes an incoming invoice, then uses the RFQ system to
7856+ // map the BTC amount to the amount of asset units for the specified asset ID.
7857+ func (r * rpcServer ) DecodeAssetPayReq (ctx context.Context ,
7858+ payReq * tchrpc.AssetPayReq ) (* tchrpc.AssetPayReqResponse , error ) {
7859+
7860+ if r .cfg .PriceOracle == nil {
7861+ return nil , fmt .Errorf ("price oracle is not set" )
7862+ }
7863+
7864+ // First, we'll perform some basic input validation.
7865+ switch {
7866+ case len (payReq .AssetId ) == 0 :
7867+ return nil , fmt .Errorf ("asset ID must be specified" )
7868+
7869+ case len (payReq .AssetId ) != 32 :
7870+ return nil , fmt .Errorf ("asset ID must be 32 bytes, " +
7871+ "was %d" , len (payReq .AssetId ))
7872+
7873+ case len (payReq .PayReqString ) == 0 :
7874+ return nil , fmt .Errorf ("payment request must be specified" )
7875+ }
7876+
7877+ var (
7878+ resp tchrpc.AssetPayReqResponse
7879+ assetID asset.ID
7880+ )
7881+
7882+ copy (assetID [:], payReq .AssetId )
7883+
7884+ // With the inputs validated, we'll first call out to lnd to decode the
7885+ // payment request.
7886+ rpcCtx , _ , rawClient := r .cfg .Lnd .Client .RawClientWithMacAuth (ctx )
7887+ payReqInfo , err := rawClient .DecodePayReq (rpcCtx , & lnrpc.PayReqString {
7888+ PayReq : payReq .PayReqString ,
7889+ })
7890+ if err != nil {
7891+ return nil , fmt .Errorf ("unable to fetch channel: %w" , err )
7892+ }
7893+
7894+ resp .PayReq = payReqInfo
7895+
7896+ // Next, we'll fetch the information for this asset ID through the addr
7897+ // book. This'll automatically fetch the asset if needed.
7898+ assetGroup , err := r .cfg .AddrBook .QueryAssetInfo (ctx , assetID )
7899+ if err != nil {
7900+ return nil , fmt .Errorf ("unable to fetch asset info for " +
7901+ "asset_id=%x: %w" , assetID [:], err )
7902+ }
7903+
7904+ resp .GenesisInfo = & taprpc.GenesisInfo {
7905+ GenesisPoint : assetGroup .FirstPrevOut .String (),
7906+ AssetType : taprpc .AssetType (assetGroup .Type ),
7907+ Name : assetGroup .Tag ,
7908+ MetaHash : assetGroup .MetaHash [:],
7909+ AssetId : assetID [:],
7910+ }
7911+
7912+ // If this asset ID belongs to an asset group, then we'll display thiat
7913+ // information as well.
7914+ //
7915+ // nolint:lll
7916+ if assetGroup .GroupKey != nil {
7917+ groupInfo := assetGroup .GroupKey
7918+ resp .AssetGroup = & taprpc.AssetGroup {
7919+ RawGroupKey : groupInfo .RawKey .PubKey .SerializeCompressed (),
7920+ TweakedGroupKey : groupInfo .GroupPubKey .SerializeCompressed (),
7921+ TapscriptRoot : groupInfo .TapscriptRoot ,
7922+ }
7923+
7924+ if len (groupInfo .Witness ) != 0 {
7925+ resp .AssetGroup .AssetWitness , err = asset .SerializeGroupWitness (
7926+ groupInfo .Witness ,
7927+ )
7928+ if err != nil {
7929+ return nil , err
7930+ }
7931+ }
7932+ }
7933+
7934+ // Now that we have the basic invoice information, we'll query the RFQ
7935+ // system to obtain a quote to send this amount of BTC. Note that this
7936+ // doesn't factor in the fee limit, so this attempts just to map the
7937+ // sats amount to an asset unit.
7938+ numMsat := lnwire .NewMSatFromSatoshis (
7939+ btcutil .Amount (payReqInfo .NumSatoshis ),
7940+ )
7941+ targetAsset := asset .NewSpecifierOptionalGroupKey (
7942+ assetGroup .ID (), assetGroup .GroupKey ,
7943+ )
7944+ invoiceAmt , err := r .assetInvoiceAmt (ctx , targetAsset , numMsat )
7945+ if err != nil {
7946+ return nil , fmt .Errorf ("error deriving asset amount: %w" , err )
7947+ }
7948+
7949+ resp .AssetAmount = invoiceAmt
7950+
7951+ // The final piece of information we need is the decimal display
7952+ // information for this asset ID.
7953+ decDisplay , err := r .DecDisplayForAssetID (ctx , assetID )
7954+ if err != nil {
7955+ return nil , err
7956+ }
7957+
7958+ resp .DecimalDisplay = fn .MapOptionZ (
7959+ decDisplay , func (d uint32 ) * taprpc.DecimalDisplay {
7960+ return & taprpc.DecimalDisplay {
7961+ DecimalDisplay : d ,
7962+ }
7963+ },
7964+ )
7965+
7966+ return & resp , nil
7967+ }
0 commit comments