Skip to content

Commit 02eefe0

Browse files
author
ffranr
authored
Merge pull request #1160 from lightninglabs/rfq-pass-rate-expiry-to-price-oracle
Ensure that asset rate hint expiry timestamps are passed to price oracle
2 parents 2639a99 + 71d2293 commit 02eefe0

File tree

10 files changed

+298
-206
lines changed

10 files changed

+298
-206
lines changed

rfq/negotiator.go

Lines changed: 39 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,8 @@ func NewNegotiator(cfg NegotiatorCfg) (*Negotiator, error) {
106106
// queryBidFromPriceOracle queries the price oracle for a bid price. It returns
107107
// an appropriate outgoing response message which should be sent to the peer.
108108
func (n *Negotiator) queryBidFromPriceOracle(peer route.Vertex,
109-
assetId *asset.ID, assetGroupKey *btcec.PublicKey,
110-
assetAmount uint64) (*rfqmath.BigIntFixedPoint, uint64, error) {
109+
assetId *asset.ID, assetGroupKey *btcec.PublicKey, assetAmount uint64,
110+
assetRateHint fn.Option[rfqmsg.AssetRate]) (*rfqmsg.AssetRate, error) {
111111

112112
// TODO(ffranr): Optionally accept a peer's proposed ask price as an
113113
// arg to this func and pass it to the price oracle. The price oracle
@@ -120,31 +120,31 @@ func (n *Negotiator) queryBidFromPriceOracle(peer route.Vertex,
120120
defer cancel()
121121

122122
oracleResponse, err := n.cfg.PriceOracle.QueryBidPrice(
123-
ctx, assetId, assetGroupKey, assetAmount,
123+
ctx, assetId, assetGroupKey, assetAmount, assetRateHint,
124124
)
125125
if err != nil {
126-
return nil, 0, fmt.Errorf("failed to query price oracle for "+
126+
return nil, fmt.Errorf("failed to query price oracle for "+
127127
"bid: %w", err)
128128
}
129129

130130
// Now we will check for an error in the response from the price oracle.
131131
// If present, we will convert it to a string and return it as an error.
132132
if oracleResponse.Err != nil {
133-
return nil, 0, fmt.Errorf("failed to query price oracle for "+
133+
return nil, fmt.Errorf("failed to query price oracle for "+
134134
"bid price: %s", oracleResponse.Err)
135135
}
136136

137137
// By this point, the price oracle did not return an error or a bid
138138
// price. We will therefore return an error.
139-
if oracleResponse.AssetRate.Coefficient.ToUint64() == 0 {
140-
return nil, 0, fmt.Errorf("price oracle did not specify a " +
139+
if oracleResponse.AssetRate.Rate.ToUint64() == 0 {
140+
return nil, fmt.Errorf("price oracle did not specify a " +
141141
"bid price")
142142
}
143143

144144
// TODO(ffranr): Check that the bid price is reasonable.
145145
// TODO(ffranr): Ensure that the expiry time is valid and sufficient.
146146

147-
return &oracleResponse.AssetRate, oracleResponse.Expiry, nil
147+
return &oracleResponse.AssetRate, nil
148148
}
149149

150150
// HandleOutgoingBuyOrder handles an outgoing buy order by constructing buy
@@ -161,13 +161,14 @@ func (n *Negotiator) HandleOutgoingBuyOrder(buyOrder BuyOrder) error {
161161
// We calculate a proposed bid price for our peer's
162162
// consideration. If a price oracle is not specified we will
163163
// skip this step.
164-
var assetRateBid fn.Option[rfqmath.BigIntFixedPoint]
164+
var assetRateHint fn.Option[rfqmsg.AssetRate]
165165

166166
if n.cfg.PriceOracle != nil {
167167
// Query the price oracle for a bid price.
168-
rate, _, err := n.queryBidFromPriceOracle(
168+
assetRate, err := n.queryBidFromPriceOracle(
169169
*buyOrder.Peer, buyOrder.AssetID,
170170
buyOrder.AssetGroupKey, buyOrder.MinAssetAmount,
171+
fn.None[rfqmsg.AssetRate](),
171172
)
172173
if err != nil {
173174
// If we fail to query the price oracle for a
@@ -178,13 +179,13 @@ func (n *Negotiator) HandleOutgoingBuyOrder(buyOrder BuyOrder) error {
178179
"request: %v", err)
179180
}
180181

181-
assetRateBid = fn.Some[rfqmath.BigIntFixedPoint](*rate)
182+
assetRateHint = fn.Some[rfqmsg.AssetRate](*assetRate)
182183
}
183184

184185
request, err := rfqmsg.NewBuyRequest(
185186
*buyOrder.Peer, buyOrder.AssetID,
186187
buyOrder.AssetGroupKey, buyOrder.MinAssetAmount,
187-
assetRateBid,
188+
assetRateHint,
188189
)
189190
if err != nil {
190191
err := fmt.Errorf("unable to create buy request "+
@@ -215,39 +216,38 @@ func (n *Negotiator) HandleOutgoingBuyOrder(buyOrder BuyOrder) error {
215216
// peer.
216217
func (n *Negotiator) queryAskFromPriceOracle(peer *route.Vertex,
217218
assetId *asset.ID, assetGroupKey *btcec.PublicKey, assetAmount uint64,
218-
suggestedAssetRate fn.Option[rfqmath.BigIntFixedPoint]) (
219-
*rfqmath.BigIntFixedPoint, uint64, error) {
219+
assetRateHint fn.Option[rfqmsg.AssetRate]) (*rfqmsg.AssetRate, error) {
220220

221221
// Query the price oracle for an asking price.
222222
ctx, cancel := n.WithCtxQuitNoTimeout()
223223
defer cancel()
224224

225225
oracleResponse, err := n.cfg.PriceOracle.QueryAskPrice(
226-
ctx, assetId, assetGroupKey, assetAmount, suggestedAssetRate,
226+
ctx, assetId, assetGroupKey, assetAmount, assetRateHint,
227227
)
228228
if err != nil {
229-
return nil, 0, fmt.Errorf("failed to query price oracle for "+
229+
return nil, fmt.Errorf("failed to query price oracle for "+
230230
"ask price: %w", err)
231231
}
232232

233233
// Now we will check for an error in the response from the price oracle.
234234
// If present, we will convert it to a string and return it as an error.
235235
if oracleResponse.Err != nil {
236-
return nil, 0, fmt.Errorf("failed to query price oracle for "+
236+
return nil, fmt.Errorf("failed to query price oracle for "+
237237
"ask price: %s", oracleResponse.Err)
238238
}
239239

240240
// By this point, the price oracle did not return an error or an asking
241241
// price. We will therefore return an error.
242-
if oracleResponse.AssetRate.Coefficient.ToUint64() == 0 {
243-
return nil, 0, fmt.Errorf("price oracle did not specify an " +
242+
if oracleResponse.AssetRate.Rate.Coefficient.ToUint64() == 0 {
243+
return nil, fmt.Errorf("price oracle did not specify an " +
244244
"asset to BTC rate")
245245
}
246246

247247
// TODO(ffranr): Check that the asking price is reasonable.
248248
// TODO(ffranr): Ensure that the expiry time is valid and sufficient.
249249

250-
return &oracleResponse.AssetRate, oracleResponse.Expiry, nil
250+
return &oracleResponse.AssetRate, nil
251251
}
252252

253253
// HandleIncomingBuyRequest handles an incoming asset buy quote request.
@@ -310,9 +310,9 @@ func (n *Negotiator) HandleIncomingBuyRequest(
310310
defer n.Wg.Done()
311311

312312
// Query the price oracle for an asking price.
313-
assetRate, rateExpiry, err := n.queryAskFromPriceOracle(
313+
assetRate, err := n.queryAskFromPriceOracle(
314314
nil, request.AssetID, request.AssetGroupKey,
315-
request.AssetAmount, request.SuggestedAssetRate,
315+
request.AssetAmount, request.AssetRateHint,
316316
)
317317
if err != nil {
318318
// Send a reject message to the peer.
@@ -330,8 +330,9 @@ func (n *Negotiator) HandleIncomingBuyRequest(
330330
}
331331

332332
// Construct and send a buy accept message.
333+
expiry := uint64(assetRate.Expiry.Unix())
333334
msg := rfqmsg.NewBuyAcceptFromRequest(
334-
request, *assetRate, rateExpiry,
335+
request, assetRate.Rate, expiry,
335336
)
336337
sendOutgoingMsg(msg)
337338
}()
@@ -405,9 +406,9 @@ func (n *Negotiator) HandleIncomingSellRequest(
405406
// Query the price oracle for a bid price. This is the price we
406407
// are willing to pay for the asset that our peer is trying to
407408
// sell to us.
408-
assetRate, rateExpiry, err := n.queryBidFromPriceOracle(
409+
assetRate, err := n.queryBidFromPriceOracle(
409410
request.Peer, request.AssetID, request.AssetGroupKey,
410-
request.AssetAmount,
411+
request.AssetAmount, request.AssetRateHint,
411412
)
412413
if err != nil {
413414
// Send a reject message to the peer.
@@ -425,8 +426,9 @@ func (n *Negotiator) HandleIncomingSellRequest(
425426
}
426427

427428
// Construct and send a sell accept message.
429+
expiry := uint64(assetRate.Expiry.Unix())
428430
msg := rfqmsg.NewSellAcceptFromRequest(
429-
request, *assetRate, rateExpiry,
431+
request, assetRate.Rate, expiry,
430432
)
431433
sendOutgoingMsg(msg)
432434
}()
@@ -448,14 +450,14 @@ func (n *Negotiator) HandleOutgoingSellOrder(order SellOrder) {
448450
// We calculate a proposed ask price for our peer's
449451
// consideration. If a price oracle is not specified we will
450452
// skip this step.
451-
var assetRate fn.Option[rfqmath.BigIntFixedPoint]
453+
var assetRateHint fn.Option[rfqmsg.AssetRate]
452454

453455
if n.cfg.PriceOracle != nil {
454456
// Query the price oracle for an asking price.
455-
rate, _, err := n.queryAskFromPriceOracle(
457+
assetRate, err := n.queryAskFromPriceOracle(
456458
order.Peer, order.AssetID, order.AssetGroupKey,
457459
order.MaxAssetAmount,
458-
fn.None[rfqmath.BigIntFixedPoint](),
460+
fn.None[rfqmsg.AssetRate](),
459461
)
460462
if err != nil {
461463
err := fmt.Errorf("negotiator failed to "+
@@ -464,12 +466,12 @@ func (n *Negotiator) HandleOutgoingSellOrder(order SellOrder) {
464466
return
465467
}
466468

467-
assetRate = fn.Some[rfqmath.BigIntFixedPoint](*rate)
469+
assetRateHint = fn.Some[rfqmsg.AssetRate](*assetRate)
468470
}
469471

470472
request, err := rfqmsg.NewSellRequest(
471473
*order.Peer, order.AssetID, order.AssetGroupKey,
472-
order.MaxAssetAmount, assetRate,
474+
order.MaxAssetAmount, assetRateHint,
473475
)
474476
if err != nil {
475477
err := fmt.Errorf("unable to create sell request "+
@@ -566,10 +568,9 @@ func (n *Negotiator) HandleIncomingBuyAccept(msg rfqmsg.BuyAccept,
566568
// We will sanity check that price by querying our price oracle
567569
// for an ask price. We will then compare the ask price returned
568570
// by the price oracle with the ask price provided by the peer.
569-
assetRate, _, err := n.queryAskFromPriceOracle(
571+
assetRate, err := n.queryAskFromPriceOracle(
570572
&msg.Peer, msg.Request.AssetID, nil,
571-
msg.Request.AssetAmount,
572-
fn.None[rfqmath.BigIntFixedPoint](),
573+
msg.Request.AssetAmount, fn.None[rfqmsg.AssetRate](),
573574
)
574575
if err != nil {
575576
// The price oracle returned an error. We will return
@@ -601,7 +602,7 @@ func (n *Negotiator) HandleIncomingBuyAccept(msg rfqmsg.BuyAccept,
601602
big.NewInt(0).SetUint64(n.cfg.AcceptPriceDeviationPpm),
602603
)
603604
acceptablePrice := msg.AssetRate.WithinTolerance(
604-
*assetRate, tolerance,
605+
assetRate.Rate, tolerance,
605606
)
606607
if !acceptablePrice {
607608
// The price is not within the acceptable tolerance.
@@ -691,9 +692,9 @@ func (n *Negotiator) HandleIncomingSellAccept(msg rfqmsg.SellAccept,
691692
// We will sanity check that price by querying our price oracle
692693
// for a bid price. We will then compare the bid price returned
693694
// by the price oracle with the bid price provided by the peer.
694-
assetRate, _, err := n.queryBidFromPriceOracle(
695+
assetRate, err := n.queryBidFromPriceOracle(
695696
msg.Peer, msg.Request.AssetID, nil,
696-
msg.Request.AssetAmount,
697+
msg.Request.AssetAmount, msg.Request.AssetRateHint,
697698
)
698699
if err != nil {
699700
// The price oracle returned an error. We will return
@@ -725,7 +726,7 @@ func (n *Negotiator) HandleIncomingSellAccept(msg rfqmsg.SellAccept,
725726
big.NewInt(0).SetUint64(n.cfg.AcceptPriceDeviationPpm),
726727
)
727728
acceptablePrice := msg.AssetRate.WithinTolerance(
728-
*assetRate, tolerance,
729+
assetRate.Rate, tolerance,
729730
)
730731
if !acceptablePrice {
731732
// The price is not within the acceptable bounds.

0 commit comments

Comments
 (0)