@@ -101,6 +101,73 @@ func isSupportedSubjectAsset(subjectAsset *oraclerpc.AssetSpecifier) bool {
101101 return false
102102}
103103
104+ // getPurchaseRate returns the buy (purchase) rate for the asset. The unit of
105+ // the rate is the number of TAP asset units per BTC.
106+ //
107+ // Suppose our TAP asset is a USD stablecoin. To support liquidity and precision
108+ // in rate conversion, we mint 1,000,000 TAP asset units per 1.00 USD. Wallet
109+ // software must therefore display 1 USD for every 1,000,000 TAP asset units.
110+ // In effect, wallet software must divide TAP asset units by 1,000,000 (= 10^6)
111+ // to show the correct USD value. We call the exponent in this conversion factor
112+ // (in this example it's 6) the `decimalDisplay` of the asset. The
113+ // decimal display for a given asset is immutable and defined at minting.
114+ //
115+ // All rates returned by the price oracle service to tapd nodes are expressed as
116+ // TAP asset units per BTC.
117+ //
118+ // Suppose the real-world price of 1 BTC is $42,000.16. To express this as a
119+ // rate in TAP asset units per BTC, multiply by the decimal display conversion
120+ // factor (10^decimalDisplay):
121+ //
122+ // realWorldPrice = 42,000.16
123+ // decimalDisplay = 6
124+ // tapAssetUnitsPerBtc = realWorldPrice * (10^decimalDisplay)
125+ //
126+ // = 42,000.16 * 1,000,000
127+ // = 42,000,160,000
128+ //
129+ // Therefore, the price oracle should return the rate:
130+ //
131+ // rfqmath.NewBigIntFixedPoint(42_000_160_000, 0)
132+ //
133+ // ## When is the FixedPoint representation useful?
134+ //
135+ // Suppose another TAP asset is highly valuable, and the buy rate is
136+ // 0.00001 TAP asset units per BTC. Spending 1 BTC would return 0.00001 TAP
137+ // asset units of our valuable asset.
138+ //
139+ // To maintain integer-based communication, representing such small fractional
140+ // rates directly is impractical due to floating-point precision issues.
141+ // Instead, the price oracle returns a FixedPoint representation with integer
142+ // components.
143+ //
144+ // A FixedPoint number `F` consists of a coefficient `C` and scale exponent `s`:
145+ //
146+ // F = C * (10^-s)
147+ //
148+ // Another way to express this is:
149+ //
150+ // F = C / (10^s)
151+ //
152+ // Now, we know F = 0.00001, and we are free to choose any integer pair (C, s)
153+ // that satisfies this equation. One simple choice is to let C = 1 and s = 5,
154+ // since 1 * 10^-5 = 0.00001.
155+ //
156+ // So the rate can be represented as:
157+ //
158+ // rfqmath.NewBigIntFixedPoint(1, 5)
159+ func getPurchaseRate () rfqmath.BigIntFixedPoint {
160+ return rfqmath .NewBigIntFixedPoint (42_000_160_000 , 0 )
161+ }
162+
163+ // getSaleRate returns the sell/sale rate for the asset. The units of the
164+ // rate is the number of TAP asset units per BTC.
165+ //
166+ // NOTE: see getPurchaseRate for more information.
167+ func getSaleRate () rfqmath.BigIntFixedPoint {
168+ return rfqmath .NewBigIntFixedPoint (39_000_220_000 , 0 )
169+ }
170+
104171// getAssetRates returns the asset rates for a given transaction type and
105172// subject asset max amount.
106173func getAssetRates (transactionType oraclerpc.TransactionType ,
@@ -109,21 +176,9 @@ func getAssetRates(transactionType oraclerpc.TransactionType,
109176 // Determine the rate based on the transaction type.
110177 var subjectAssetRate rfqmath.BigIntFixedPoint
111178 if transactionType == oraclerpc .TransactionType_PURCHASE {
112- // As an example, the purchase rate is $42,000 per BTC. To
113- // increase precision, we represent this as 42 billion
114- // taproot asset units per BTC. Therefore, we scale the
115- // $42,000 per BTC rate by a factor of 10^6.
116- subjectAssetRate = rfqmath .FixedPointFromUint64 [rfqmath.BigInt ](
117- 42_000 , 6 ,
118- )
179+ subjectAssetRate = getPurchaseRate ()
119180 } else {
120- // Our example sell rate will be lower at $40,000 per BTC. This
121- // rate will be represented as 40 billion taproot asset units
122- // per BTC. Therefore, we scale the $40,000 per BTC rate by a
123- // factor of 10^6.
124- subjectAssetRate = rfqmath .FixedPointFromUint64 [rfqmath.BigInt ](
125- 40_000 , 6 ,
126- )
181+ subjectAssetRate = getSaleRate ()
127182 }
128183
129184 // Set the rate expiry to 5 minutes by default.
0 commit comments