@@ -8,6 +8,7 @@ import io.provenance.explorer.config.ExplorerProperties.Companion.UTILITY_TOKEN_
8
8
import io.provenance.explorer.config.ResourceNotFoundException
9
9
import io.provenance.explorer.domain.core.logger
10
10
import io.provenance.explorer.domain.entities.AccountRecord
11
+ import io.provenance.explorer.domain.entities.NavEvent
11
12
import io.provenance.explorer.domain.entities.NavEventsRecord
12
13
import io.provenance.explorer.domain.entities.PulseCacheRecord
13
14
import io.provenance.explorer.domain.entities.TxCacheRecord
@@ -170,30 +171,21 @@ class PulseMetricService(
170
171
private fun isScheduledTask (): Boolean =
171
172
Thread .currentThread().name.startsWith(" scheduling-" )
172
173
173
- /* *
174
- * Periodically refreshes the pulse cache
175
- */
176
- fun refreshCache () = transaction {
177
- val threadName = Thread .currentThread().name
178
- logger.info(" Refreshing pulse cache for thread $threadName " )
179
- PulseCacheType .entries.filter {
180
- it != PulseCacheType .PULSE_ASSET_VOLUME_SUMMARY_METRIC &&
181
- it != PulseCacheType .PULSE_ASSET_PRICE_SUMMARY_METRIC
182
- }
183
- .forEach { type ->
184
- pulseMetric(type)
185
- }
186
-
187
- pulseAssetSummaries()
188
-
189
- logger.info(" Pulse cache refreshed for thread $threadName " )
190
- }
191
-
192
174
private fun denomSupplyCache (denom : String ) =
193
175
denomCurrentSupplyCache.get(denom) {
194
176
assetService.getCurrentSupply(denom).toBigDecimal()
195
177
} ? : BigDecimal .ZERO
196
178
179
+ private fun latestPulseAssetPrice (denom : String ) =
180
+ fromPulseMetricCache(
181
+ LocalDateTime .now().startOfDay().toLocalDate(),
182
+ PulseCacheType .PULSE_ASSET_PRICE_SUMMARY_METRIC , denom
183
+ )?.amount ? :
184
+ fromPulseMetricCache(
185
+ LocalDateTime .now().minusDays(1 ).startOfDay().toLocalDate(),
186
+ PulseCacheType .PULSE_ASSET_PRICE_SUMMARY_METRIC , denom
187
+ )?.amount ? : BigDecimal .ZERO
188
+
197
189
/* *
198
190
* Returns the current hash market cap metric comparing the previous day's market cap
199
191
* to the current day's market cap
@@ -214,56 +206,6 @@ class PulseMetricService(
214
206
? : throw ResourceNotFoundException (" No quote found for $quote " )
215
207
}
216
208
217
- /* *
218
- * Returns the current hash metrics for the given type
219
- */
220
- fun hashMetric (type : PulseCacheType , bustCache : Boolean = false) =
221
- fetchOrBuildCacheFromDataSource(
222
- type = type
223
- ) {
224
- val latestHashPrice =
225
- tokenService.getTokenLatest()?.quote?.get(USD_UPPER )?.price
226
- ? : BigDecimal .ZERO
227
-
228
- when (type) {
229
- PulseCacheType .HASH_STAKED_METRIC -> {
230
- val staked = validatorService.getStakingValidators(ACTIVE )
231
- .sumOf { it.tokenCount }
232
- .divide(UTILITY_TOKEN_BASE_MULTIPLIER ).roundWhole()
233
- PulseMetric .build(
234
- base = UTILITY_TOKEN ,
235
- amount = staked,
236
- quote = USD_UPPER ,
237
- quoteAmount = latestHashPrice.times(staked)
238
- )
239
- }
240
-
241
- PulseCacheType .HASH_CIRCULATING_METRIC -> {
242
- val tokenSupply = tokenService.totalSupply()
243
- .divide(UTILITY_TOKEN_BASE_MULTIPLIER )
244
- .roundWhole()
245
- PulseMetric .build(
246
- base = UTILITY_TOKEN ,
247
- amount = tokenSupply,
248
- quote = USD_UPPER ,
249
- quoteAmount = latestHashPrice.times(tokenSupply)
250
- )
251
- }
252
-
253
- PulseCacheType .HASH_SUPPLY_METRIC -> {
254
- val tokenSupply = tokenService.maxSupply()
255
- .divide(UTILITY_TOKEN_BASE_MULTIPLIER )
256
- .roundWhole()
257
- PulseMetric .build(
258
- base = UTILITY_TOKEN ,
259
- amount = tokenSupply
260
- )
261
- }
262
-
263
- else -> throw ResourceNotFoundException (" Invalid hash metric request for type $type " )
264
- }
265
- }
266
-
267
209
/* *
268
210
* Returns global market cap aka total AUM
269
211
*/
@@ -327,12 +269,21 @@ class PulseMetricService(
327
269
type = PulseCacheType .PULSE_RECEIVABLES_METRIC
328
270
) { // TODO technically correct assuming only metadata nav events are receivables
329
271
NavEventsRecord .getNavEvents(
330
- fromDate = LocalDateTime .now().startOfDay()
331
- ).filter { it.source == " metadata" && it.scopeId != null } // gross
272
+ fromDate = LocalDateTime .now().startOfDay(),
273
+ source = " metadata" ,
274
+ priceDenoms = listOf (USD_LOWER )
275
+ ).sortedWith(compareBy<NavEvent > { it.scopeId }.thenByDescending { it.blockTime })
276
+ .distinctBy {
277
+ // will keep the first occurrence which is the latest price event
278
+ it.scopeId
279
+ }
332
280
.sumOf { it.priceAmount!! }.toBigDecimal().let {
281
+ /* so it turns out that the `usd` in metadata nav events
282
+ use 3 decimal places - :|
283
+ */
333
284
PulseMetric .build(
334
285
base = USD_UPPER ,
335
- amount = it
286
+ amount = it.times(inversePowerOfTen( 3 ))
336
287
)
337
288
}
338
289
}
@@ -398,51 +349,31 @@ class PulseMetricService(
398
349
}
399
350
400
351
/* *
401
- * Returns the total committed assets value across all exchanges - a sum of all commitments
352
+ * Returns the total committed assets value across all exchanges
353
+ * as a sum of all commitments
402
354
*/
403
355
private fun exchangeCommittedAssetsValue (): PulseMetric =
404
356
fetchOrBuildCacheFromDataSource(
405
357
type = PulseCacheType .PULSE_COMMITTED_ASSETS_VALUE_METRIC
406
358
) {
407
- committedAssetTotals().values.sumOf { it }.let {
359
+ committedAssetTotals().map {
360
+ // convert amount to appropriate denom decimal
361
+ var dE = denomExponent(it.key)
362
+ if (dE == 0 && it.key.lowercase().contains(USD_LOWER )) {
363
+ dE = 6
364
+ }
365
+ Pair (it.key, it.value.times(inversePowerOfTen(dE)))
366
+ }.map {
367
+ // get price of the asset
368
+ Pair (it.first, it.second.times(latestPulseAssetPrice(it.first)))
369
+ }.sumOf { it.second }.let {
408
370
PulseMetric .build(
409
371
base = USD_UPPER ,
410
- amount = it.times(inversePowerOfTen( 6 ))
372
+ amount = it
411
373
)
412
374
}
413
375
}
414
376
415
- private fun todoPulse (): PulseMetric =
416
- PulseMetric .build(
417
- base = UTILITY_TOKEN ,
418
- amount = BigDecimal .ZERO
419
- )
420
-
421
- /* *
422
- * Returns the pulse metric for the given type - pulse metrics are "global"
423
- * metrics that are not specific to Hash
424
- */
425
- fun pulseMetric (type : PulseCacheType ): PulseMetric {
426
- return when (type) {
427
- PulseCacheType .HASH_MARKET_CAP_METRIC -> hashMarketCapMetric()
428
- PulseCacheType .HASH_STAKED_METRIC -> hashMetric(type)
429
- PulseCacheType .HASH_CIRCULATING_METRIC -> hashMetric(type)
430
- PulseCacheType .HASH_SUPPLY_METRIC -> hashMetric(type)
431
- PulseCacheType .PULSE_MARKET_CAP_METRIC -> pulseMarketCap()
432
- PulseCacheType .PULSE_TRANSACTION_VOLUME_METRIC -> transactionVolume()
433
- PulseCacheType .PULSE_RECEIVABLES_METRIC -> pulseReceivableValue()
434
- PulseCacheType .PULSE_TRADE_SETTLEMENT_METRIC -> pulseTradesSettled()
435
- PulseCacheType .PULSE_TRADE_VALUE_SETTLED_METRIC -> pulseTradeValueSettled()
436
- PulseCacheType .PULSE_PARTICIPANTS_METRIC -> totalParticipants()
437
- PulseCacheType .PULSE_COMMITTED_ASSETS_METRIC -> exchangeCommittedAssets()
438
- PulseCacheType .PULSE_COMMITTED_ASSETS_VALUE_METRIC -> exchangeCommittedAssetsValue()
439
- PulseCacheType .PULSE_DEMOCRATIZED_PRIME_POOLS_METRIC -> todoPulse()
440
- PulseCacheType .PULSE_MARGIN_LOANS_METRIC -> todoPulse()
441
- PulseCacheType .PULSE_FEES_AUCTIONS_METRIC -> todoPulse()
442
- else -> throw ResourceNotFoundException (" Invalid pulse metric request for type $type " )
443
- }
444
- }
445
-
446
377
/* *
447
378
* Asset denom metadata from chain
448
379
*/
@@ -457,6 +388,9 @@ class PulseMetricService(
457
388
private fun denomExponent (denomMetadata : Bank .Metadata ) =
458
389
denomMetadata.denomUnitsList.firstOrNull { it.exponent != 0 }?.exponent
459
390
391
+ private fun denomExponent (denom : String ) =
392
+ denomExponent(pulseAssetDenomMetadata(denom)) ? : 0
393
+
460
394
/* *
461
395
* Returns the inverse power of ten for the given exponent because I
462
396
* mostly don't like to divide to move decimal places
@@ -489,8 +423,108 @@ class PulseMetricService(
489
423
exchangeGrpcClient.totalCommittedAssetTotals()
490
424
}
491
425
426
+ private fun todoPulse (): PulseMetric =
427
+ PulseMetric .build(
428
+ base = UTILITY_TOKEN ,
429
+ amount = BigDecimal .ZERO
430
+ )
431
+
432
+ /* *
433
+ * Periodically refreshes the pulse cache
434
+ */
435
+ fun refreshCache () = transaction {
436
+ val threadName = Thread .currentThread().name
437
+ logger.info(" Refreshing pulse cache for thread $threadName " )
438
+ PulseCacheType .entries.filter {
439
+ it != PulseCacheType .PULSE_ASSET_VOLUME_SUMMARY_METRIC &&
440
+ it != PulseCacheType .PULSE_ASSET_PRICE_SUMMARY_METRIC
441
+ }
442
+ .forEach { type ->
443
+ pulseMetric(type)
444
+ }
445
+
446
+ pulseAssetSummaries()
447
+
448
+ logger.info(" Pulse cache refreshed for thread $threadName " )
449
+ }
450
+
451
+ /* *
452
+ * Returns the current hash metrics for the given type
453
+ */
454
+ fun hashMetric (type : PulseCacheType , bustCache : Boolean = false) =
455
+ fetchOrBuildCacheFromDataSource(
456
+ type = type
457
+ ) {
458
+ val latestHashPrice =
459
+ tokenService.getTokenLatest()?.quote?.get(USD_UPPER )?.price
460
+ ? : BigDecimal .ZERO
461
+
462
+ when (type) {
463
+ PulseCacheType .HASH_STAKED_METRIC -> {
464
+ val staked = validatorService.getStakingValidators(ACTIVE )
465
+ .sumOf { it.tokenCount }
466
+ .divide(UTILITY_TOKEN_BASE_MULTIPLIER ).roundWhole()
467
+ PulseMetric .build(
468
+ base = UTILITY_TOKEN ,
469
+ amount = staked,
470
+ quote = USD_UPPER ,
471
+ quoteAmount = latestHashPrice.times(staked)
472
+ )
473
+ }
474
+
475
+ PulseCacheType .HASH_CIRCULATING_METRIC -> {
476
+ val tokenSupply = tokenService.totalSupply()
477
+ .divide(UTILITY_TOKEN_BASE_MULTIPLIER )
478
+ .roundWhole()
479
+ PulseMetric .build(
480
+ base = UTILITY_TOKEN ,
481
+ amount = tokenSupply,
482
+ quote = USD_UPPER ,
483
+ quoteAmount = latestHashPrice.times(tokenSupply)
484
+ )
485
+ }
486
+
487
+ PulseCacheType .HASH_SUPPLY_METRIC -> {
488
+ val tokenSupply = tokenService.maxSupply()
489
+ .divide(UTILITY_TOKEN_BASE_MULTIPLIER )
490
+ .roundWhole()
491
+ PulseMetric .build(
492
+ base = UTILITY_TOKEN ,
493
+ amount = tokenSupply
494
+ )
495
+ }
496
+
497
+ else -> throw ResourceNotFoundException (" Invalid hash metric request for type $type " )
498
+ }
499
+ }
500
+
501
+ /* *
502
+ * Returns the pulse metric for the given type - pulse metrics are "global"
503
+ * metrics that are not specific to Hash
504
+ */
505
+ fun pulseMetric (type : PulseCacheType ): PulseMetric {
506
+ return when (type) {
507
+ PulseCacheType .HASH_MARKET_CAP_METRIC -> hashMarketCapMetric()
508
+ PulseCacheType .HASH_STAKED_METRIC -> hashMetric(type)
509
+ PulseCacheType .HASH_CIRCULATING_METRIC -> hashMetric(type)
510
+ PulseCacheType .HASH_SUPPLY_METRIC -> hashMetric(type)
511
+ PulseCacheType .PULSE_MARKET_CAP_METRIC -> pulseMarketCap()
512
+ PulseCacheType .PULSE_TRANSACTION_VOLUME_METRIC -> transactionVolume()
513
+ PulseCacheType .PULSE_RECEIVABLES_METRIC -> pulseReceivableValue()
514
+ PulseCacheType .PULSE_TRADE_SETTLEMENT_METRIC -> pulseTradesSettled()
515
+ PulseCacheType .PULSE_TRADE_VALUE_SETTLED_METRIC -> pulseTradeValueSettled()
516
+ PulseCacheType .PULSE_PARTICIPANTS_METRIC -> totalParticipants()
517
+ PulseCacheType .PULSE_COMMITTED_ASSETS_METRIC -> exchangeCommittedAssets()
518
+ PulseCacheType .PULSE_COMMITTED_ASSETS_VALUE_METRIC -> exchangeCommittedAssetsValue()
519
+ PulseCacheType .PULSE_DEMOCRATIZED_PRIME_POOLS_METRIC -> todoPulse()
520
+ PulseCacheType .PULSE_MARGIN_LOANS_METRIC -> todoPulse()
521
+ PulseCacheType .PULSE_FEES_AUCTIONS_METRIC -> todoPulse()
522
+ else -> throw ResourceNotFoundException (" Invalid pulse metric request for type $type " )
523
+ }
524
+ }
525
+
492
526
/* *
493
- * TODO - this is problematic because it assumes all assets are USD-based
527
+ * TODO - this is problematic because it assumes all assets are USD quoted
494
528
*/
495
529
fun pulseAssetSummaries (): List <PulseAssetSummary > =
496
530
committedAssetTotals().keys.distinct().map { denom ->
@@ -630,11 +664,7 @@ class PulseMetricService(
630
664
val denomMetadata = pulseAssetDenomMetadata(denom)
631
665
val denomExp = denomExponent(denomMetadata) ? : 1
632
666
val denomPow = inversePowerOfTen(denomExp)
633
- val denomPrice =
634
- fromPulseMetricCache(
635
- LocalDateTime .now().minusDays(1 ).startOfDay().toLocalDate(),
636
- PulseCacheType .PULSE_ASSET_PRICE_SUMMARY_METRIC , denom
637
- )?.amount ? : BigDecimal .ZERO
667
+ val denomPrice = latestPulseAssetPrice(denom)
638
668
639
669
PagedResults (
640
670
pages = pr.pages,
0 commit comments