|
9 | 9 | "time" |
10 | 10 |
|
11 | 11 | "github.com/btcsuite/btcd/btcutil" |
| 12 | + "github.com/lightninglabs/taproot-assets/rfqmath" |
12 | 13 | "github.com/lightninglabs/taproot-assets/tapcfg" |
13 | 14 | "github.com/lightninglabs/taproot-assets/taprpc" |
14 | 15 | "github.com/lightninglabs/taproot-assets/taprpc/priceoraclerpc" |
@@ -184,6 +185,66 @@ func (c *TapdClient) GetAssetName(ctx context.Context, |
184 | 185 | return assetName, nil |
185 | 186 | } |
186 | 187 |
|
| 188 | +// GetAssetPrice returns the price of an asset in satoshis. NOTE: this currently |
| 189 | +// uses the rfq process for the asset price. A future implementation should |
| 190 | +// use a price oracle to not spam a peer. |
| 191 | +func (c *TapdClient) GetAssetPrice(ctx context.Context, assetId string, |
| 192 | + assetAmt uint64, satMinAmt btcutil.Amount) (btcutil.Amount, |
| 193 | + error) { |
| 194 | + |
| 195 | + // We'll allow a short rfq expiry as we'll only use this rfq to |
| 196 | + // gauge a price. |
| 197 | + rfqExpiry := time.Now().Add(time.Minute).Unix() |
| 198 | + |
| 199 | + // First we'll rfq a random peer for the asset. |
| 200 | + rfq, err := c.RfqClient.AddAssetSellOrder( |
| 201 | + ctx, &rfqrpc.AddAssetSellOrderRequest{ |
| 202 | + AssetSpecifier: &rfqrpc.AssetSpecifier{ |
| 203 | + Id: &rfqrpc.AssetSpecifier_AssetIdStr{ |
| 204 | + AssetIdStr: assetId, |
| 205 | + }, |
| 206 | + }, |
| 207 | + PaymentMaxAmt: uint64(satMinAmt), |
| 208 | + Expiry: uint64(rfqExpiry), |
| 209 | + TimeoutSeconds: uint32(c.cfg.RFQtimeout.Seconds()), |
| 210 | + }) |
| 211 | + if err != nil { |
| 212 | + return 0, err |
| 213 | + } |
| 214 | + if rfq.GetInvalidQuote() != nil { |
| 215 | + return 0, fmt.Errorf("invalid RFQ: %v", rfq.GetInvalidQuote()) |
| 216 | + } |
| 217 | + |
| 218 | + if rfq.GetRejectedQuote() != nil { |
| 219 | + return 0, fmt.Errorf("rejected RFQ: %v", rfq.GetRejectedQuote()) |
| 220 | + } |
| 221 | + |
| 222 | + acceptedRes := rfq.GetAcceptedQuote() |
| 223 | + if acceptedRes == nil { |
| 224 | + return 0, fmt.Errorf("no accepted quote") |
| 225 | + } |
| 226 | + |
| 227 | + // We'll use the accepted quote to calculate the price. |
| 228 | + return getSatsFromAssetAmt(assetAmt, acceptedRes.BidAssetRate) |
| 229 | +} |
| 230 | + |
| 231 | +// getSatsFromAssetAmt returns the amount in satoshis for the given asset amount |
| 232 | +// and asset rate. |
| 233 | +func getSatsFromAssetAmt(assetAmt uint64, assetRate *rfqrpc.FixedPoint) ( |
| 234 | + btcutil.Amount, error) { |
| 235 | + |
| 236 | + rateFP, err := rfqrpc.UnmarshalFixedPoint(assetRate) |
| 237 | + if err != nil { |
| 238 | + return 0, fmt.Errorf("cannot unmarshal asset rate: %w", err) |
| 239 | + } |
| 240 | + |
| 241 | + assetUnits := rfqmath.NewBigIntFixedPoint(assetAmt, 0) |
| 242 | + |
| 243 | + msatAmt := rfqmath.UnitsToMilliSatoshi(assetUnits, *rateFP) |
| 244 | + |
| 245 | + return msatAmt.ToSatoshis(), nil |
| 246 | +} |
| 247 | + |
187 | 248 | // getPaymentMaxAmount returns the milisat amount we are willing to pay for the |
188 | 249 | // payment. |
189 | 250 | func getPaymentMaxAmount(satAmount btcutil.Amount, feeLimitMultiplier float64) ( |
|
0 commit comments