1
0
Fork 0

Compare commits

..

No commits in common. "5b843a794846edf2f1abd1143f43524a34ea1634" and "3fbb87d821ac8af1784274709d07e490d1471b57" have entirely different histories.

7 changed files with 48 additions and 267 deletions

View File

@ -1,12 +0,0 @@
groups:
- name: helium-blockchain-exporter-rules
interval: 1m
rules:
- record: hotspot:helium_hotspot_rewards_hnt:increase15m
expr: sum by (account, hotspot, hotspot_name) (increase(helium_hotspot_rewards_hnt_total[15m]))
- record: type:helium_hotspot_activity:floor_increase15m
expr: sum by (account, hotspot, hotspot_name, type) (floor(increase(helium_hotspot_activity_total[15m])))
- record: hotspot:helium_hotspot_rewards_hnt:increase1d
expr: sum by (account, hotspot, hotspot_name) (increase(helium_hotspot_rewards_hnt_total[1d]))
- record: hotspot:helium_hotspot_rewards_hnt:increase1w
expr: sum by (account, hotspot, hotspot_name) (increase(helium_hotspot_rewards_hnt_total[1w]))

View File

@ -3,7 +3,6 @@ package main
import ( import (
"flag" "flag"
"log" "log"
"math"
"net/http" "net/http"
"os" "os"
"strconv" "strconv"
@ -222,14 +221,6 @@ var (
), ),
prometheus.GaugeValue, prometheus.GaugeValue,
} }
hotspotSynced = metricInfo{
prometheus.NewDesc(
prometheus.BuildFQName(namespace, "hotspot", "synced"),
"Whether a hotspot is synced with the blockchain.",
commonHotspotLabels, nil,
),
prometheus.GaugeValue,
}
hotspotRelayed = metricInfo{ hotspotRelayed = metricInfo{
prometheus.NewDesc( prometheus.NewDesc(
prometheus.BuildFQName(namespace, "hotspot", "relayed"), prometheus.BuildFQName(namespace, "hotspot", "relayed"),
@ -254,22 +245,6 @@ var (
), ),
prometheus.GaugeValue, prometheus.GaugeValue,
} }
hotspot5dWitnesses = metricInfo{
prometheus.NewDesc(
prometheus.BuildFQName(namespace, "hotspot", "5d_witnesses"),
"The number of hotspots that witnessed this hotspot in the last 5 days.",
commonHotspotLabels, nil,
),
prometheus.GaugeValue,
}
hotspot5dWitnessed = metricInfo{
prometheus.NewDesc(
prometheus.BuildFQName(namespace, "hotspot", "5d_witnessed"),
"The number of hotspots this hotspot witnessed in the last 5 days.",
commonHotspotLabels, nil,
),
prometheus.GaugeValue,
}
hotspotGeocodeInfo = metricInfo{ hotspotGeocodeInfo = metricInfo{
prometheus.NewDesc( prometheus.NewDesc(
prometheus.BuildFQName(namespace, "hotspot", "geocode_info"), prometheus.BuildFQName(namespace, "hotspot", "geocode_info"),
@ -334,12 +309,9 @@ func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
ch <- accountWithdrawalsHnt.Desc ch <- accountWithdrawalsHnt.Desc
ch <- hotspotUp.Desc ch <- hotspotUp.Desc
ch <- hotspotSynced.Desc
ch <- hotspotRelayed.Desc ch <- hotspotRelayed.Desc
ch <- hotspotBlocks.Desc ch <- hotspotBlocks.Desc
ch <- hotspotRewardsScale.Desc ch <- hotspotRewardsScale.Desc
ch <- hotspot5dWitnesses.Desc
ch <- hotspot5dWitnessed.Desc
ch <- hotspotGeocodeInfo.Desc ch <- hotspotGeocodeInfo.Desc
ch <- hotspotAntennaInfo.Desc ch <- hotspotAntennaInfo.Desc
ch <- hotspotActivity.Desc ch <- hotspotActivity.Desc
@ -477,7 +449,6 @@ func (e *Exporter) collectAccountRewardsTotalMetrics(wg *sync.WaitGroup, ch chan
func (e *Exporter) collectAccountTransactionsMetrics(wg *sync.WaitGroup, ch chan<- prometheus.Metric, account *Account) { func (e *Exporter) collectAccountTransactionsMetrics(wg *sync.WaitGroup, ch chan<- prometheus.Metric, account *Account) {
defer wg.Done() defer wg.Done()
// We optimize our queries by asking the api only the activities we car about.
activityTypes := []string{ activityTypes := []string{
"add_gateway_v1", "add_gateway_v1",
"assert_location_v1", "assert_location_v1",
@ -492,25 +463,13 @@ func (e *Exporter) collectAccountTransactionsMetrics(wg *sync.WaitGroup, ch chan
"unstake_validator_v1", "unstake_validator_v1",
} }
// We can only want to allow a single instance of the routine doing // we can only want to allow a single instance of the routine doing
// calculations on the deposited and widthdrawn total. // calculations on the deposited and widthdrawn total
account.Tx.UpdateLock.Lock() account.Tx.UpdateLock.Lock()
defer account.Tx.UpdateLock.Unlock() defer account.Tx.UpdateLock.Unlock()
// We want to keep in memory the timestamp of the last activity we now := time.Now()
// received from the api. We cannot do something naive like [lastscrape, now] activities, err := heliumapi.GetActivityForAccount(account.Address, activityTypes, &account.Tx.LastUpdate, &now)
// because the api can take a few seconds to sync with the chain and
// we can miss some activities by doing it that way.
lastActivityTime := account.Tx.LastUpdate.Unix()
updateLastActivityTime := func(newTime int64) {
if lastActivityTime < newTime {
lastActivityTime = newTime
}
}
// add 1 second to the last update time to avoid querying the same activity twice
minTime := account.Tx.LastUpdate.Add(time.Second * 1)
activities, err := heliumapi.GetActivityForAccount(account.Address, activityTypes, &minTime, nil)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
return return
@ -519,15 +478,12 @@ func (e *Exporter) collectAccountTransactionsMetrics(wg *sync.WaitGroup, ch chan
// impl based on https://github.com/helium/hotspot-app/blob/918563fba84d1abf4554a43a4d42bb838d017bd3/src/features/wallet/root/useActivityItem.tsx#L336 // impl based on https://github.com/helium/hotspot-app/blob/918563fba84d1abf4554a43a4d42bb838d017bd3/src/features/wallet/root/useActivityItem.tsx#L336
for _, activity := range activities.AddGatewayV1 { for _, activity := range activities.AddGatewayV1 {
account.Tx.WithdrawalTotal += activity.StakingFee account.Tx.WithdrawalTotal += activity.StakingFee
updateLastActivityTime(activity.Time)
} }
for _, activity := range activities.AssertLocationV1 { for _, activity := range activities.AssertLocationV1 {
account.Tx.WithdrawalTotal += activity.StakingFee account.Tx.WithdrawalTotal += activity.StakingFee
updateLastActivityTime(activity.Time)
} }
for _, activity := range activities.AssertLocationV2 { for _, activity := range activities.AssertLocationV2 {
account.Tx.WithdrawalTotal += activity.StakingFee account.Tx.WithdrawalTotal += activity.StakingFee
updateLastActivityTime(activity.Time)
} }
for _, activity := range activities.PaymentV1 { for _, activity := range activities.PaymentV1 {
if activity.Payer == account.Address { if activity.Payer == account.Address {
@ -535,7 +491,6 @@ func (e *Exporter) collectAccountTransactionsMetrics(wg *sync.WaitGroup, ch chan
} else { } else {
account.Tx.DepositTotal += activity.Amount account.Tx.DepositTotal += activity.Amount
} }
updateLastActivityTime(activity.Time)
} }
for _, activity := range activities.PaymentV2 { for _, activity := range activities.PaymentV2 {
if activity.Payer == account.Address { if activity.Payer == account.Address {
@ -551,27 +506,22 @@ func (e *Exporter) collectAccountTransactionsMetrics(wg *sync.WaitGroup, ch chan
} }
} }
} }
updateLastActivityTime(activity.Time)
} }
for _, activity := range activities.RewardsV1 { for _, activity := range activities.RewardsV1 {
for _, reward := range activity.Rewards { for _, reward := range activity.Rewards {
account.Tx.DepositTotal += reward.Amount account.Tx.DepositTotal += reward.Amount
} }
updateLastActivityTime(activity.Time)
} }
for _, activity := range activities.RewardsV2 { for _, activity := range activities.RewardsV2 {
for _, reward := range activity.Rewards { for _, reward := range activity.Rewards {
account.Tx.DepositTotal += reward.Amount account.Tx.DepositTotal += reward.Amount
} }
updateLastActivityTime(activity.Time)
} }
for _, activity := range activities.StakeValidatorV1 { for _, activity := range activities.StakeValidatorV1 {
account.Tx.WithdrawalTotal += activity.Stake account.Tx.WithdrawalTotal += activity.Stake
updateLastActivityTime(activity.Time)
} }
for _, activity := range activities.TokenBurnV1 { for _, activity := range activities.TokenBurnV1 {
account.Tx.WithdrawalTotal += activity.Amount account.Tx.WithdrawalTotal += activity.Amount
updateLastActivityTime(activity.Time)
} }
for _, activity := range activities.TransferHotspotV1 { for _, activity := range activities.TransferHotspotV1 {
if activity.Buyer == account.Address { if activity.Buyer == account.Address {
@ -579,13 +529,11 @@ func (e *Exporter) collectAccountTransactionsMetrics(wg *sync.WaitGroup, ch chan
} else { } else {
account.Tx.DepositTotal += activity.AmountToSeller account.Tx.DepositTotal += activity.AmountToSeller
} }
updateLastActivityTime(activity.Time)
} }
for _, activity := range activities.UnstakeValidatorV1 { for _, activity := range activities.UnstakeValidatorV1 {
account.Tx.WithdrawalTotal += activity.StakeAmount account.Tx.WithdrawalTotal += activity.StakeAmount
updateLastActivityTime(activity.Time)
} }
account.Tx.LastUpdate = time.Unix(lastActivityTime, 0) account.Tx.LastUpdate = now
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
accountDepositsHnt.Desc, accountDepositsHnt.Type, float64(account.Tx.DepositTotal)/blockchain_hnt_factor, accountDepositsHnt.Desc, accountDepositsHnt.Type, float64(account.Tx.DepositTotal)/blockchain_hnt_factor,
@ -609,10 +557,7 @@ func (e *Exporter) collectHotspotMetrics(wg *sync.WaitGroup, ch chan<- prometheu
for _, hotspotData := range *hotspotsForAddress { for _, hotspotData := range *hotspotsForAddress {
// collect hotspot metric requiring extra queries in a new routine // collect hotspot metric requiring extra queries in a new routine
wg.Add(5) wg.Add(2)
go e.collectHotspotSyncedMetrics(wg, ch, account, hotspotData)
go e.collectHotspotWitnessesMetrics(wg, ch, account, hotspotData)
go e.collectHotspotWitnessedMetrics(wg, ch, account, hotspotData)
go e.collectHotspotActivityMetrics(wg, ch, account, hotspotData) go e.collectHotspotActivityMetrics(wg, ch, account, hotspotData)
go e.collectHotspotRewardsMetrics(wg, ch, account, hotspotData) go e.collectHotspotRewardsMetrics(wg, ch, account, hotspotData)
@ -643,68 +588,8 @@ func (e *Exporter) collectHotspotMetrics(wg *sync.WaitGroup, ch chan<- prometheu
} }
} }
// collectSyncedMetrics calculate if the hotspot is in sync with the blockchain
func (e *Exporter) collectHotspotSyncedMetrics(wg *sync.WaitGroup, ch chan<- prometheus.Metric, account *Account, hotspotData heliumapi.Hotspot) {
defer wg.Done()
isSynced := 0.
if hotspotData.Status.Online == "online" && hotspotData.Status.Timestamp != "" {
statusTime, err := time.Parse(time.RFC3339Nano, hotspotData.Status.Timestamp)
if err != nil {
log.Println(err)
return
}
heightAtUpdate, err := heliumapi.GetHeight(&statusTime)
if err != nil {
log.Println(err)
return
}
// if the block height and the height reported by the hotspot is within 10 blocks, we consider it in sync
if math.Abs(float64(hotspotData.Status.Height-heightAtUpdate)) <= 10 {
isSynced = 1.
}
}
ch <- prometheus.MustNewConstMetric(
hotspotSynced.Desc, hotspotActivity.Type, isSynced,
account.Address, hotspotData.Address, hotspotData.Name,
)
}
// collectHotspotWitnessesMetrics collect the total number witnesses of a hotspot in the last 5d
func (e *Exporter) collectHotspotWitnessesMetrics(wg *sync.WaitGroup, ch chan<- prometheus.Metric, account *Account, hotspotData heliumapi.Hotspot) {
defer wg.Done()
hotspotWitnesses, err := heliumapi.GetWitnessesForHotspot(hotspotData.Address)
if err != nil {
log.Println(err)
return
}
ch <- prometheus.MustNewConstMetric(
hotspot5dWitnesses.Desc, hotspotActivity.Type, float64(len(*hotspotWitnesses)),
account.Address, hotspotData.Address, hotspotData.Name,
)
}
// collectHotspotWitnessedMetrics collect the total number hotspots witnessed in the last 5d
func (e *Exporter) collectHotspotWitnessedMetrics(wg *sync.WaitGroup, ch chan<- prometheus.Metric, account *Account, hotspotData heliumapi.Hotspot) {
defer wg.Done()
hotspotWitnessed, err := heliumapi.GetWitnessedForHotspot(hotspotData.Address)
if err != nil {
log.Println(err)
return
}
ch <- prometheus.MustNewConstMetric(
hotspot5dWitnessed.Desc, hotspotActivity.Type, float64(len(*hotspotWitnessed)),
account.Address, hotspotData.Address, hotspotData.Name,
)
}
// collectHotspotActivityMetrics collect the total number of activities executed by a hotspot from the helium api // collectHotspotActivityMetrics collect the total number of activities executed by a hotspot from the helium api
func (e *Exporter) collectHotspotActivityMetrics(wg *sync.WaitGroup, ch chan<- prometheus.Metric, account *Account, hotspotData heliumapi.Hotspot) { func (e *Exporter) collectHotspotActivityMetrics(wg *sync.WaitGroup, ch chan<- prometheus.Metric, account *Account, hotspotData heliumapi.AccountHotspot) {
defer wg.Done() defer wg.Done()
hotspotActivityForAddress, err := heliumapi.GetHotspotActivityCounts(hotspotData.Address) hotspotActivityForAddress, err := heliumapi.GetHotspotActivityCounts(hotspotData.Address)
@ -722,7 +607,7 @@ func (e *Exporter) collectHotspotActivityMetrics(wg *sync.WaitGroup, ch chan<- p
} }
// collectHotspotRewardsMetrics collect the total rewards accumulated by a hotspot from the helium api // collectHotspotRewardsMetrics collect the total rewards accumulated by a hotspot from the helium api
func (e *Exporter) collectHotspotRewardsMetrics(wg *sync.WaitGroup, ch chan<- prometheus.Metric, account *Account, hotspotData heliumapi.Hotspot) { func (e *Exporter) collectHotspotRewardsMetrics(wg *sync.WaitGroup, ch chan<- prometheus.Metric, account *Account, hotspotData heliumapi.AccountHotspot) {
defer wg.Done() defer wg.Done()
hotspotRewardTotalsForAddress, err := heliumapi.GetRewardsTotalForHotspot(hotspotData.Address, &e.StartTime, nil) hotspotRewardTotalsForAddress, err := heliumapi.GetRewardsTotalForHotspot(hotspotData.Address, &e.StartTime, nil)

View File

@ -110,7 +110,7 @@ func GetRewardTotalsForAccount(account string, minTime *time.Time, maxTime *time
} }
// query https://docs.helium.com/api/blockchain/accounts#hotspots-for-account // query https://docs.helium.com/api/blockchain/accounts#hotspots-for-account
func GetHotspotsForAccount(account string) (*[]Hotspot, error) { func GetHotspotsForAccount(account string) (*[]AccountHotspot, error) {
path := "/v1/accounts/" + account + "/hotspots" path := "/v1/accounts/" + account + "/hotspots"
// query the api // query the api

View File

@ -14,16 +14,8 @@ type Activities struct {
UnstakeValidatorV1 []UnstakeValidatorV1 UnstakeValidatorV1 []UnstakeValidatorV1
} }
type BaseActivity struct {
Hash string `json:"hash"`
Time int64 `json:"time"`
StartEpoch int `json:"start_epoch"`
EndEpoch int `json:"end_epoch"`
Height int `json:"height"`
}
type AddGatewayV1 struct { type AddGatewayV1 struct {
BaseActivity Hash string `json:"hash"`
Fee int `json:"fee"` Fee int `json:"fee"`
Owner string `json:"owner"` Owner string `json:"owner"`
Payer string `json:"payer"` Payer string `json:"payer"`
@ -32,7 +24,7 @@ type AddGatewayV1 struct {
} }
type AssertLocationV1 struct { type AssertLocationV1 struct {
BaseActivity Hash string `json:"hash"`
Fee int `json:"fee"` Fee int `json:"fee"`
Nonce int `json:"nonce"` Nonce int `json:"nonce"`
Owner string `json:"owner"` Owner string `json:"owner"`
@ -43,7 +35,7 @@ type AssertLocationV1 struct {
} }
type AssertLocationV2 struct { type AssertLocationV2 struct {
BaseActivity Hash string `json:"hash"`
Fee int `json:"fee"` Fee int `json:"fee"`
Gain int `json:"gain"` Gain int `json:"gain"`
Nonce int `json:"nonce"` Nonce int `json:"nonce"`
@ -56,7 +48,7 @@ type AssertLocationV2 struct {
} }
type PaymentV1 struct { type PaymentV1 struct {
BaseActivity Hash string `json:"hash"`
Amount int `json:"amount"` Amount int `json:"amount"`
Fee int `json:"fee"` Fee int `json:"fee"`
Nonce int `json:"nonce"` Nonce int `json:"nonce"`
@ -65,7 +57,7 @@ type PaymentV1 struct {
} }
type PaymentV2 struct { type PaymentV2 struct {
BaseActivity Hash string `json:"hash"`
Fee int `json:"fee"` Fee int `json:"fee"`
Nonce int `json:"nonce"` Nonce int `json:"nonce"`
Payer string `json:"payer"` Payer string `json:"payer"`
@ -84,31 +76,31 @@ type reward struct {
} }
type RewardsV1 struct { type RewardsV1 struct {
BaseActivity Hash string `json:"hash"`
StartEpoch int `json:"start_epoch"` StartEpoch int `json:"start_epoch"`
EndEpoch int `json:"end_epoch"` EndEpoch int `json:"end_epoch"`
Rewards []reward `json:"rewards"` Rewards []reward `json:"rewards"`
} }
type RewardsV2 struct { type RewardsV2 struct {
BaseActivity Hash string `json:"hash"`
StartEpoch int `json:"start_epoch"` StartEpoch int `json:"start_epoch"`
EndEpoch int `json:"end_epoch"` EndEpoch int `json:"end_epoch"`
Rewards []reward `json:"rewards"` Rewards []reward `json:"rewards"`
} }
type StakeValidatorV1 struct { type StakeValidatorV1 struct {
BaseActivity
Address string `json:"address"` Address string `json:"address"`
Fee int `json:"fee"` Fee int `json:"fee"`
Hash string `json:"hash"`
Owner string `json:"owner"` Owner string `json:"owner"`
Stake int `json:"stake"` Stake int `json:"stake"`
OwnerSignature string `json:"owner_signature"` OwnerSignature string `json:"owner_signature"`
} }
type TokenBurnV1 struct { type TokenBurnV1 struct {
BaseActivity
Fee int `json:"fee"` Fee int `json:"fee"`
Hash string `json:"hash"`
Memo string `json:"memo"` Memo string `json:"memo"`
Nonce int `json:"nonce"` Nonce int `json:"nonce"`
Payer string `json:"payer"` Payer string `json:"payer"`
@ -117,7 +109,7 @@ type TokenBurnV1 struct {
} }
type TransferHotspotV1 struct { type TransferHotspotV1 struct {
BaseActivity Hash string `json:"hash"`
Fee int `json:"fee"` Fee int `json:"fee"`
Buyer string `json:"buyer"` Buyer string `json:"buyer"`
Seller string `json:"seller"` Seller string `json:"seller"`
@ -127,11 +119,11 @@ type TransferHotspotV1 struct {
} }
type UnstakeValidatorV1 struct { type UnstakeValidatorV1 struct {
BaseActivity
Address string `json:"address"` Address string `json:"address"`
Owner string `json:"owner"` Owner string `json:"owner"`
OwnerSignature string `json:"owner_signature"` OwnerSignature string `json:"owner_signature"`
Fee int `json:"fee"` Fee int `json:"fee"`
StakeAmount int `json:"stake_amount"` StakeAmount int `json:"stake_amount"`
StakeReleaseHeight int `json:"stake_release_height"` StakeReleaseHeight int `json:"stake_release_height"`
Hash string `json:"hash"`
} }

View File

@ -1,31 +0,0 @@
package heliumapi
import (
"encoding/json"
"fmt"
"time"
)
// query https://docs.helium.com/api/blockchain/blocks/#height
func GetHeight(maxTime *time.Time) (int, error) {
path := "/v1/blocks/height"
params := map[string]string{}
if maxTime != nil {
params["max_time"] = maxTime.UTC().Format("2006-01-02T15:04:05Z")
}
// query the api
respBody, err := getHeliumApi(path, &params)
if err != nil {
return -1, err
}
// unmarshal the response
respobject := HeightResp{}
err = json.Unmarshal(respBody, &respobject)
if err != nil {
return -1, fmt.Errorf("failed to unmarshal response from %v: %v", path, err)
}
return respobject.Data.Height, nil
}

View File

@ -52,43 +52,3 @@ func GetRewardsTotalForHotspot(hotspot string, minTime *time.Time, maxTime *time
return &respobject.Data, nil return &respobject.Data, nil
} }
// query https://docs.helium.com/api/blockchain/hotspots#witnesses-for-a-hotspot
func GetWitnessesForHotspot(hotspot string) (*[]Hotspot, error) {
path := "/v1/hotspots/" + hotspot + "/witnesses"
// query the api
respBody, err := getHeliumApi(path, nil)
if err != nil {
return nil, err
}
// unmarshal the response
respobject := WitnessesResp{}
err = json.Unmarshal(respBody, &respobject)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal response from %v: %v", path, err)
}
return &respobject.Data, nil
}
// query https://docs.helium.com/api/blockchain/hotspots#witnessed-for-a-hotspot
func GetWitnessedForHotspot(hotspot string) (*[]Hotspot, error) {
path := "/v1/hotspots/" + hotspot + "/witnessed"
// query the api
respBody, err := getHeliumApi(path, nil)
if err != nil {
return nil, err
}
// unmarshal the response
respobject := WitnessedResp{}
err = json.Unmarshal(respBody, &respobject)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal response from %v: %v", path, err)
}
return &respobject.Data, nil
}

View File

@ -4,46 +4,6 @@ type AccountResp struct {
Data Account `json:"data"` Data Account `json:"data"`
} }
type AccountHotspotsResp struct {
Data []Hotspot `json:"data"`
}
type ActivityCounts map[string]int
type ActivityCountsResp struct {
Data ActivityCounts
}
type RewardTotalResp struct {
Meta struct {
MinTime string `json:"min_time"`
MaxTime string `json:"max_time"`
} `json:"meta"`
Data RewardTotal `json:"data"`
}
type CurrentOraclePriceResp struct {
Data CurrentOraclePrice `json:"data"`
}
type BlockchainStatsResp struct {
Data BlockchainStats `json:"data"`
}
type WitnessesResp struct {
Data []Hotspot `json:"data"`
}
type HeightResp struct {
Data struct {
Height int `json:"height"`
} `json:"data"`
}
type WitnessedResp struct {
Data []Hotspot `json:"data"`
}
type Account struct { type Account struct {
Address string `json:"address"` Address string `json:"address"`
Balance int `json:"balance"` Balance int `json:"balance"`
@ -55,7 +15,11 @@ type Account struct {
SpeculativeNonce int `json:"speculative_nonce"` SpeculativeNonce int `json:"speculative_nonce"`
} }
type Hotspot struct { type AccountHotspotsResp struct {
Data []AccountHotspot `json:"data"`
}
type AccountHotspot struct {
Lng float64 `json:"lng"` Lng float64 `json:"lng"`
Lat float64 `json:"lat"` Lat float64 `json:"lat"`
TimestampAdded string `json:"timestamp_added"` TimestampAdded string `json:"timestamp_added"`
@ -93,6 +57,21 @@ type Hotspot struct {
Address string `json:"address"` Address string `json:"address"`
} }
type ActivityCountsResp struct {
Data ActivityCounts
}
type ActivityCounts map[string]int
type RewardTotalResp struct {
Meta struct {
MinTime string `json:"min_time"`
MaxTime string `json:"max_time"`
} `json:"meta"`
Data RewardTotal `json:"data"`
}
type RewardTotal struct { type RewardTotal struct {
Total float64 `json:"total"` Total float64 `json:"total"`
Sum float64 `json:"sum"` Sum float64 `json:"sum"`
@ -103,11 +82,19 @@ type RewardTotal struct {
Avg float64 `json:"avg"` Avg float64 `json:"avg"`
} }
type CurrentOraclePriceResp struct {
Data CurrentOraclePrice `json:"data"`
}
type CurrentOraclePrice struct { type CurrentOraclePrice struct {
Price int `json:"price"` Price int `json:"price"`
Block int `json:"block"` Block int `json:"block"`
} }
type BlockchainStatsResp struct {
Data BlockchainStats `json:"data"`
}
type BlockchainStats struct { type BlockchainStats struct {
BlockTime struct { BlockTime struct {
LastDay struct { LastDay struct {