Skip to content

Commit 9f066a4

Browse files
committed
rpcserver: allow adding invoice with sats amount
1 parent 331ac78 commit 9f066a4

File tree

1 file changed

+92
-17
lines changed

1 file changed

+92
-17
lines changed

rpcserver.go

Lines changed: 92 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7637,6 +7637,29 @@ func (r *rpcServer) AddInvoice(ctx context.Context,
76377637
if len(req.AssetId) != sha256.Size {
76387638
return nil, fmt.Errorf("asset ID must be 32 bytes")
76397639
}
7640+
7641+
// Let's make sure that only one type of amount is set, in order to
7642+
// avoid ambiguous behavior. This field dictates the actual value of the
7643+
// invoice so let's be strict and only allow one possible value to be
7644+
// set.
7645+
if req.AssetAmount > 0 && (req.InvoiceRequest.ValueMsat > 0 ||
7646+
req.InvoiceRequest.Value > 0) {
7647+
7648+
return nil, fmt.Errorf("cannot set both asset amount and sats" +
7649+
"amount")
7650+
}
7651+
7652+
if req.InvoiceRequest.ValueMsat > 0 && req.InvoiceRequest.Value > 0 {
7653+
return nil, fmt.Errorf("cannot set both sats and msats")
7654+
}
7655+
7656+
// In order to avoid repeating the following check let's assign it to a
7657+
// boolean for easier access.
7658+
var satsMode bool
7659+
if req.InvoiceRequest.Value > 0 || req.InvoiceRequest.ValueMsat > 0 {
7660+
satsMode = true
7661+
}
7662+
76407663
var assetID asset.ID
76417664
copy(assetID[:], req.AssetId)
76427665

@@ -7676,13 +7699,44 @@ func (r *rpcServer) AddInvoice(ctx context.Context,
76767699
time.Duration(expirySeconds) * time.Second,
76777700
)
76787701

7702+
maxUnits := req.AssetAmount
7703+
amtMsat := lnwire.MilliSatoshi(0)
7704+
7705+
// If the invoice defines the desired amount in satoshis, we need to
7706+
// query our oracle first to get an estimation on the asset rate. This
7707+
// will help us establish a quote with the correct amount of asset
7708+
// units.
7709+
if satsMode {
7710+
amtMsat = lnwire.MilliSatoshi(req.InvoiceRequest.ValueMsat +
7711+
req.InvoiceRequest.Value*(1000))
7712+
7713+
oracleRes, err := r.cfg.PriceOracle.QueryBidPrice(
7714+
ctx, specifier, fn.None[uint64](), fn.Some(amtMsat),
7715+
fn.None[rfqmsg.AssetRate](),
7716+
)
7717+
if err != nil {
7718+
return nil, err
7719+
}
7720+
7721+
if oracleRes.Err != nil {
7722+
return nil, fmt.Errorf("cannot query oracle: %v",
7723+
oracleRes.Err.Error())
7724+
}
7725+
7726+
assetUnits := rfqmath.MilliSatoshiToUnits(
7727+
amtMsat, oracleRes.AssetRate.Rate,
7728+
)
7729+
7730+
maxUnits = assetUnits.ToUint64()
7731+
}
7732+
76797733
resp, err := r.AddAssetBuyOrder(ctx, &rfqrpc.AddAssetBuyOrderRequest{
76807734
AssetSpecifier: &rfqrpc.AssetSpecifier{
76817735
Id: &rfqrpc.AssetSpecifier_AssetId{
76827736
AssetId: assetID[:],
76837737
},
76847738
},
7685-
AssetMaxAmt: req.AssetAmount,
7739+
AssetMaxAmt: maxUnits,
76867740
Expiry: uint64(expiryTimestamp.Unix()),
76877741
PeerPubKey: peerPubKey[:],
76887742
TimeoutSeconds: uint32(
@@ -7712,17 +7766,6 @@ func (r *rpcServer) AddInvoice(ctx context.Context,
77127766
return nil, fmt.Errorf("unexpected response type: %T", r)
77137767
}
77147768

7715-
// If the invoice is for an asset unit amount smaller than the minimal
7716-
// transportable amount, we'll return an error, as it wouldn't be
7717-
// payable by the network.
7718-
if acceptedQuote.MinTransportableUnits > req.AssetAmount {
7719-
return nil, fmt.Errorf("cannot create invoice over %d asset "+
7720-
"units, as the minimal transportable amount is %d "+
7721-
"units with the current rate of %v units/BTC",
7722-
req.AssetAmount, acceptedQuote.MinTransportableUnits,
7723-
acceptedQuote.AskAssetRate)
7724-
}
7725-
77267769
// Now that we have the accepted quote, we know the amount in Satoshi
77277770
// that we need to pay. We can now update the invoice with this amount.
77287771
//
@@ -7735,12 +7778,44 @@ func (r *rpcServer) AddInvoice(ctx context.Context,
77357778
err)
77367779
}
77377780

7738-
// Convert the asset amount into a fixed-point.
7739-
assetAmount := rfqmath.NewBigIntFixedPoint(req.AssetAmount, 0)
7781+
invUnits := req.AssetAmount
7782+
7783+
// If the invoice was created over a satoshi amount, we need to
7784+
// calculate the units.
7785+
if satsMode {
7786+
invUnits = rfqmath.MilliSatoshiToUnits(
7787+
amtMsat, *askAssetRate,
7788+
).ToUint64()
7789+
}
7790+
7791+
// If the invoice is for an asset unit amount smaller than the minimal
7792+
// transportable amount, we'll return an error, as it wouldn't be
7793+
// payable by the network.
7794+
if acceptedQuote.MinTransportableUnits > invUnits {
7795+
return nil, fmt.Errorf("cannot create invoice over %d asset "+
7796+
"units, as the minimal transportable amount is %d "+
7797+
"units with the current rate of %v units/BTC",
7798+
invUnits, acceptedQuote.MinTransportableUnits,
7799+
acceptedQuote.AskAssetRate)
7800+
}
7801+
7802+
switch {
7803+
case satsMode:
7804+
amtMsat := lnwire.MilliSatoshi(req.InvoiceRequest.ValueMsat +
7805+
req.InvoiceRequest.Value*(1000))
7806+
7807+
iReq.ValueMsat = int64(amtMsat)
7808+
7809+
default:
7810+
// Convert the asset amount into a fixed-point.
7811+
assetAmount := rfqmath.NewBigIntFixedPoint(req.AssetAmount, 0)
77407812

7741-
// Calculate the invoice amount in msat.
7742-
valMsat := rfqmath.UnitsToMilliSatoshi(assetAmount, *askAssetRate)
7743-
iReq.ValueMsat = int64(valMsat)
7813+
// Calculate the invoice amount in msat.
7814+
valMsat := rfqmath.UnitsToMilliSatoshi(
7815+
assetAmount, *askAssetRate,
7816+
)
7817+
iReq.ValueMsat = int64(valMsat)
7818+
}
77447819

77457820
// The last step is to create a hop hint that includes the fake SCID of
77467821
// the quote, alongside the channel's routing policy. We need to choose

0 commit comments

Comments
 (0)