diff --git a/api/worker.go b/api/worker.go
index 1dd8d8d7..230d7176 100644
--- a/api/worker.go
+++ b/api/worker.go
@@ -1010,24 +1010,27 @@ func (w *Worker) getEthereumContractBalanceFromBlockchain(addrDesc, contract bch
}, nil
}
-// GetContractBaseRate returns contract rate in base coin from the ticker or DB at the blocktime. Zero blocktime means now.
-func (w *Worker) GetContractBaseRate(ticker *common.CurrencyRatesTicker, contract string, blocktime int64) (float64, bool) {
+// GetContractBaseRate returns contract rate in base coin from the ticker or DB at the timestamp. Zero timestamp means now.
+func (w *Worker) GetContractBaseRate(ticker *common.CurrencyRatesTicker, token string, timestamp int64) (float64, bool) {
if ticker == nil {
return 0, false
}
- rate, found := ticker.GetTokenRate(contract)
+ rate, found := ticker.GetTokenRate(token)
if !found {
- var date time.Time
- if blocktime == 0 {
- date = time.Now().UTC()
+ if timestamp == 0 {
+ ticker = w.fiatRates.GetCurrentTicker("", token)
} else {
- date = time.Unix(blocktime, 0).UTC()
+ tickers, err := w.fiatRates.GetTickersForTimestamps([]int64{timestamp}, "", token)
+ if err != nil || tickers == nil || len(*tickers) == 0 {
+ ticker = nil
+ } else {
+ ticker = (*tickers)[0]
+ }
}
- ticker, _ = w.db.FiatRatesFindTicker(&date, "", contract)
if ticker == nil {
return 0, false
}
- rate, found = ticker.GetTokenRate(contract)
+ rate, found = ticker.GetTokenRate(token)
}
return float64(rate), found
@@ -1564,12 +1567,13 @@ func (w *Worker) balanceHistoryForTxid(addrDesc bchain.AddressDescriptor, txid s
func (w *Worker) setFiatRateToBalanceHistories(histories BalanceHistories, currencies []string) error {
for i := range histories {
bh := &histories[i]
- t := time.Unix(int64(bh.Time), 0)
- ticker, err := w.db.FiatRatesFindTicker(&t, "", "")
- if err != nil {
- glog.Errorf("Error finding ticker by date %v. Error: %v", t, err)
+ tickers, err := w.fiatRates.GetTickersForTimestamps([]int64{int64(bh.Time)}, "", "")
+ if err != nil || tickers == nil || len(*tickers) == 0 {
+ glog.Errorf("Error finding ticker by date %v. Error: %v", bh.Time, err)
continue
- } else if ticker == nil {
+ }
+ ticker := (*tickers)[0]
+ if ticker == nil {
continue
}
if len(currencies) == 0 {
@@ -1818,16 +1822,29 @@ func removeEmpty(stringSlice []string) []string {
// getFiatRatesResult checks if CurrencyRatesTicker contains all necessary data and returns formatted result
func (w *Worker) getFiatRatesResult(currencies []string, ticker *common.CurrencyRatesTicker, token string) (*FiatTicker, error) {
if token != "" {
- if len(currencies) != 1 {
- return nil, NewAPIError("Rates for token only for a single currency", true)
- }
- rate := ticker.TokenRateInCurrency(token, currencies[0])
- if rate <= 0 {
- rate = -1
+ rates := make(map[string]float32)
+ if len(currencies) == 0 {
+ for currency := range ticker.Rates {
+ currency = strings.ToLower(currency)
+ rate := ticker.TokenRateInCurrency(token, currency)
+ if rate <= 0 {
+ rate = -1
+ }
+ rates[currency] = rate
+ }
+ } else {
+ for _, currency := range currencies {
+ currency = strings.ToLower(currency)
+ rate := ticker.TokenRateInCurrency(token, currency)
+ if rate <= 0 {
+ rate = -1
+ }
+ rates[currency] = rate
+ }
}
return &FiatTicker{
Timestamp: ticker.Timestamp.UTC().Unix(),
- Rates: map[string]float32{currencies[0]: rate},
+ Rates: rates,
}, nil
}
if len(currencies) == 0 {
@@ -1853,36 +1870,6 @@ func (w *Worker) getFiatRatesResult(currencies []string, ticker *common.Currency
}, nil
}
-// GetFiatRatesForBlockID returns fiat rates for block height or block hash
-func (w *Worker) GetFiatRatesForBlockID(blockID string, currencies []string, token string) (*FiatTicker, error) {
- var ticker *common.CurrencyRatesTicker
- bi, err := w.getBlockInfoFromBlockID(blockID)
- if err != nil {
- if err == bchain.ErrBlockNotFound {
- return nil, NewAPIError(fmt.Sprintf("Block %v not found", blockID), true)
- }
- return nil, NewAPIError(fmt.Sprintf("Block %v not found, error: %v", blockID, err), false)
- }
- dbi := &db.BlockInfo{Time: bi.Time} // get Unix timestamp from block
- tm := time.Unix(dbi.Time, 0) // convert it to Time object
- vsCurrency := ""
- currencies = removeEmpty(currencies)
- if len(currencies) == 1 {
- vsCurrency = currencies[0]
- }
- ticker, err = w.db.FiatRatesFindTicker(&tm, vsCurrency, token)
- if err != nil {
- return nil, NewAPIError(fmt.Sprintf("Error finding ticker: %v", err), false)
- } else if ticker == nil {
- return nil, NewAPIError(fmt.Sprintf("No tickers available for %s", tm), true)
- }
- result, err := w.getFiatRatesResult(currencies, ticker, token)
- if err != nil {
- return nil, err
- }
- return result, nil
-}
-
// GetCurrentFiatRates returns last available fiat rates
func (w *Worker) GetCurrentFiatRates(currencies []string, token string) (*FiatTicker, error) {
vsCurrency := ""
@@ -1927,47 +1914,64 @@ func (w *Worker) GetFiatRatesForTimestamps(timestamps []int64, currencies []stri
if len(currencies) == 1 {
vsCurrency = currencies[0]
}
-
- ret := &FiatTickers{}
- for _, timestamp := range timestamps {
- date := time.Unix(timestamp, 0)
- date = date.UTC()
- ticker, err := w.db.FiatRatesFindTicker(&date, vsCurrency, token)
- if err != nil {
- glog.Errorf("Error finding ticker for date %v. Error: %v", date, err)
- ret.Tickers = append(ret.Tickers, FiatTicker{Timestamp: date.Unix(), Rates: makeErrorRates(currencies)})
- continue
- } else if ticker == nil {
- ret.Tickers = append(ret.Tickers, FiatTicker{Timestamp: date.Unix(), Rates: makeErrorRates(currencies)})
+ tickers, err := w.fiatRates.GetTickersForTimestamps(timestamps, vsCurrency, token)
+ if err != nil {
+ return nil, err
+ }
+ if tickers == nil {
+ return nil, NewAPIError("No tickers found", true)
+ }
+ if len(*tickers) != len(timestamps) {
+ glog.Error("GetFiatRatesForTimestamps: number of tickers does not match timestamps ", len(*tickers), ", ", len(timestamps))
+ return nil, NewAPIError("No tickers found", false)
+ }
+ fiatTickers := make([]FiatTicker, len(*tickers))
+ for i, t := range *tickers {
+ if t == nil {
+ fiatTickers[i] = FiatTicker{Timestamp: timestamps[i], Rates: makeErrorRates(currencies)}
continue
}
- result, err := w.getFiatRatesResult(currencies, ticker, token)
+ result, err := w.getFiatRatesResult(currencies, t, token)
if err != nil {
if apiErr, ok := err.(*APIError); ok {
if apiErr.Public {
return nil, err
}
}
- ret.Tickers = append(ret.Tickers, FiatTicker{Timestamp: date.Unix(), Rates: makeErrorRates(currencies)})
+ fiatTickers[i] = FiatTicker{Timestamp: timestamps[i], Rates: makeErrorRates(currencies)}
continue
}
- ret.Tickers = append(ret.Tickers, *result)
+ fiatTickers[i] = *result
}
- return ret, nil
+ return &FiatTickers{Tickers: fiatTickers}, nil
+}
+
+// GetFiatRatesForBlockID returns fiat rates for block height or block hash
+func (w *Worker) GetFiatRatesForBlockID(blockID string, currencies []string, token string) (*FiatTicker, error) {
+ bi, err := w.getBlockInfoFromBlockID(blockID)
+ if err != nil {
+ if err == bchain.ErrBlockNotFound {
+ return nil, NewAPIError(fmt.Sprintf("Block %v not found", blockID), true)
+ }
+ return nil, NewAPIError(fmt.Sprintf("Block %v not found, error: %v", blockID, err), false)
+ }
+ tickers, err := w.GetFiatRatesForTimestamps([]int64{bi.Time}, currencies, token)
+ if err != nil || tickers == nil || len(tickers.Tickers) == 0 {
+ return nil, err
+ }
+ return &tickers.Tickers[0], nil
}
// GetAvailableVsCurrencies returns the list of available versus currencies for exchange rates
func (w *Worker) GetAvailableVsCurrencies(timestamp int64, token string) (*AvailableVsCurrencies, error) {
- date := time.Unix(timestamp, 0)
- date = date.UTC()
-
- ticker, err := w.db.FiatRatesFindTicker(&date, "", strings.ToLower(token))
+ tickers, err := w.fiatRates.GetTickersForTimestamps([]int64{timestamp}, "", token)
if err != nil {
return nil, NewAPIError(fmt.Sprintf("Error finding ticker: %v", err), false)
- } else if ticker == nil {
- return nil, NewAPIError(fmt.Sprintf("No tickers found for date %v.", date), true)
}
-
+ if tickers == nil || len(*tickers) == 0 {
+ return nil, NewAPIError("No tickers found", true)
+ }
+ ticker := (*tickers)[0]
keys := make([]string, 0, len(ticker.Rates))
for k := range ticker.Rates {
keys = append(keys, k)
diff --git a/bchain/coins/blockchain.go b/bchain/coins/blockchain.go
index 32eba742..0181cd25 100644
--- a/bchain/coins/blockchain.go
+++ b/bchain/coins/blockchain.go
@@ -140,19 +140,15 @@ func init() {
}
// GetCoinNameFromConfig gets coin name and coin shortcut from config file
-func GetCoinNameFromConfig(configfile string) (string, string, string, error) {
- data, err := ioutil.ReadFile(configfile)
- if err != nil {
- return "", "", "", errors.Annotatef(err, "Error reading file %v", configfile)
- }
+func GetCoinNameFromConfig(configFileContent []byte) (string, string, string, error) {
var cn struct {
CoinName string `json:"coin_name"`
CoinShortcut string `json:"coin_shortcut"`
CoinLabel string `json:"coin_label"`
}
- err = json.Unmarshal(data, &cn)
+ err := json.Unmarshal(configFileContent, &cn)
if err != nil {
- return "", "", "", errors.Annotatef(err, "Error parsing file %v", configfile)
+ return "", "", "", errors.Annotatef(err, "Error parsing config file ")
}
return cn.CoinName, cn.CoinShortcut, cn.CoinLabel, nil
}
diff --git a/blockbook.go b/blockbook.go
index 54b2dc1f..e1f48e1a 100644
--- a/blockbook.go
+++ b/blockbook.go
@@ -4,7 +4,6 @@ import (
"context"
"encoding/json"
"flag"
- "io/ioutil"
"log"
"math/rand"
"net/http"
@@ -158,7 +157,13 @@ func mainWithExitCode() int {
return exitCodeFatal
}
- coin, coinShortcut, coinLabel, err := coins.GetCoinNameFromConfig(*configFile)
+ configFileContent, err := os.ReadFile(*configFile)
+ if err != nil {
+ glog.Errorf("Error reading file %v, %v", configFile, err)
+ return exitCodeFatal
+ }
+
+ coin, coinShortcut, coinLabel, err := coins.GetCoinNameFromConfig(configFileContent)
if err != nil {
glog.Error("config: ", err)
return exitCodeFatal
@@ -274,7 +279,7 @@ func mainWithExitCode() int {
return exitCodeFatal
}
- if fiatRates, err = fiat.NewFiatRates(index, *configFile, onNewFiatRatesTicker); err != nil {
+ if fiatRates, err = fiat.NewFiatRates(index, configFileContent, onNewFiatRatesTicker); err != nil {
glog.Error("fiatRates ", err)
return exitCodeFatal
}
@@ -363,7 +368,7 @@ func mainWithExitCode() int {
if internalServer != nil || publicServer != nil || chain != nil {
// start fiat rates downloader only if not shutting down immediately
- initDownloaders(index, chain, *configFile)
+ initDownloaders(index, chain, configFileContent)
waitForSignalAndShutdown(internalServer, publicServer, chain, 10*time.Second)
}
@@ -697,24 +702,18 @@ func computeFeeStats(stopCompute chan os.Signal, blockFrom, blockTo int, db *db.
return err
}
-func initDownloaders(db *db.RocksDB, chain bchain.BlockChain, configFile string) {
+func initDownloaders(db *db.RocksDB, chain bchain.BlockChain, configFileContent []byte) {
if fiatRates.Enabled {
go fiatRates.RunDownloader()
}
- data, err := ioutil.ReadFile(configFile)
- if err != nil {
- glog.Errorf("Error reading file %v, %v", configFile, err)
- return
- }
-
var config struct {
FourByteSignatures string `json:"fourByteSignatures"`
}
- err = json.Unmarshal(data, &config)
+ err := json.Unmarshal(configFileContent, &config)
if err != nil {
- glog.Errorf("Error parsing config file %v, %v", configFile, err)
+ glog.Errorf("Error parsing config file %v, %v", *configFile, err)
return
}
diff --git a/fiat/fiat_rates.go b/fiat/fiat_rates.go
index b1f59ba8..0721197a 100644
--- a/fiat/fiat_rates.go
+++ b/fiat/fiat_rates.go
@@ -5,7 +5,6 @@ import (
"errors"
"fmt"
"math/rand"
- "os"
"strings"
"sync"
"time"
@@ -37,7 +36,7 @@ type RatesDownloaderInterface interface {
UpdateHistoricalTokenTickers() error
}
-// FiatRates stores FiatRates API parameters
+// FiatRates is used to fetch and refresh fiat rates
type FiatRates struct {
Enabled bool
periodSeconds int64
@@ -62,19 +61,15 @@ type FiatRates struct {
}
// NewFiatRates initializes the FiatRates handler
-func NewFiatRates(db *db.RocksDB, configFile string, callback OnNewFiatRatesTicker) (*FiatRates, error) {
+func NewFiatRates(db *db.RocksDB, configFileContent []byte, callback OnNewFiatRatesTicker) (*FiatRates, error) {
var config struct {
FiatRates string `json:"fiat_rates"`
FiatRatesParams string `json:"fiat_rates_params"`
FiatRatesVsCurrencies string `json:"fiat_rates_vs_currencies"`
}
- data, err := os.ReadFile(configFile)
+ err := json.Unmarshal(configFileContent, &config)
if err != nil {
- return nil, fmt.Errorf("error reading file %v, %v", configFile, err)
- }
- err = json.Unmarshal(data, &config)
- if err != nil {
- return nil, fmt.Errorf("error parsing config file %v, %v", configFile, err)
+ return nil, fmt.Errorf("error parsing config file, %v", err)
}
var fr = &FiatRates{
@@ -83,7 +78,7 @@ func NewFiatRates(db *db.RocksDB, configFile string, callback OnNewFiatRatesTick
}
if config.FiatRates == "" || config.FiatRatesParams == "" {
- glog.Infof("FiatRates config (%v) is empty, not downloading fiat rates", configFile)
+ glog.Infof("FiatRates config is empty, not downloading fiat rates")
fr.Enabled = false
return fr, nil
}
@@ -176,22 +171,30 @@ func (fr *FiatRates) getTokenTickersForTimestamps(timestamps []int64, vsCurrency
currentTicker := fr.GetCurrentTicker("", token)
tickers := make([]*common.CurrencyRatesTicker, len(timestamps))
var prevTicker *common.CurrencyRatesTicker
+ var prevTs int64
+ var err error
for i, t := range timestamps {
// check if the token is available in the current ticker - if not, return nil ticker instead of wasting time in costly DB searches
if currentTicker != nil {
var ticker *common.CurrencyRatesTicker
date := time.Unix(t, 0)
// if previously found ticker is newer than this one (token tickers may not be in DB for every day), skip search in DB
- if prevTicker != nil && !date.After(prevTicker.Timestamp) {
+ if prevTicker != nil && t >= prevTs && !date.After(prevTicker.Timestamp) {
ticker = prevTicker
+ prevTs = t
} else {
- ticker, _ := fr.db.FiatRatesFindTicker(&date, vsCurrency, token)
+ ticker, err = fr.db.FiatRatesFindTicker(&date, vsCurrency, token)
+ if err != nil {
+ return nil, err
+ }
prevTicker = ticker
+ prevTs = t
}
// if ticker not found in DB, use current ticker
if ticker == nil {
tickers[i] = currentTicker
prevTicker = currentTicker
+ prevTs = t
} else {
tickers[i] = ticker
}
@@ -202,6 +205,9 @@ func (fr *FiatRates) getTokenTickersForTimestamps(timestamps []int64, vsCurrency
// GetTickersForTimestamps returns tickers for slice of timestamps, that contain requested vsCurrency and token
func (fr *FiatRates) GetTickersForTimestamps(timestamps []int64, vsCurrency string, token string) (*[]*common.CurrencyRatesTicker, error) {
+ if !fr.Enabled {
+ return nil, nil
+ }
// token rates are not in memory, them load from DB
if token != "" {
return fr.getTokenTickersForTimestamps(timestamps, vsCurrency, token)
@@ -210,12 +216,13 @@ func (fr *FiatRates) GetTickersForTimestamps(timestamps []int64, vsCurrency stri
defer fr.mux.RUnlock()
tickers := make([]*common.CurrencyRatesTicker, len(timestamps))
var prevTicker *common.CurrencyRatesTicker
+ var prevTs int64
for i, t := range timestamps {
- dailyTs := normalizedUnix(t, secondsInDay)
+ dailyTs := ceilUnix(t, secondsInDay)
// use higher granularity only for non daily timestamps
if t != dailyTs {
if t >= fr.fiveMinutesTickersFrom && t <= fr.fiveMinutesTickersTo {
- if ticker, found := fr.fiveMinutesTickers[normalizedUnix(t, secondsInFiveMinutes)]; found && ticker != nil {
+ if ticker, found := fr.fiveMinutesTickers[ceilUnix(t, secondsInFiveMinutes)]; found && ticker != nil {
if common.IsSuitableTicker(ticker, vsCurrency, token) {
tickers[i] = ticker
continue
@@ -223,7 +230,7 @@ func (fr *FiatRates) GetTickersForTimestamps(timestamps []int64, vsCurrency stri
}
}
if t >= fr.hourlyTickersFrom && t <= fr.hourlyTickersTo {
- if ticker, found := fr.hourlyTickers[normalizedUnix(t, secondsInHour)]; found && ticker != nil {
+ if ticker, found := fr.hourlyTickers[ceilUnix(t, secondsInHour)]; found && ticker != nil {
if common.IsSuitableTicker(ticker, vsCurrency, token) {
tickers[i] = ticker
continue
@@ -231,7 +238,7 @@ func (fr *FiatRates) GetTickersForTimestamps(timestamps []int64, vsCurrency stri
}
}
}
- if prevTicker != nil && dailyTs >= prevTicker.Timestamp.Unix() {
+ if prevTicker != nil && t >= prevTs && t <= prevTicker.Timestamp.Unix() {
tickers[i] = prevTicker
continue
} else {
@@ -245,15 +252,17 @@ func (fr *FiatRates) GetTickersForTimestamps(timestamps []int64, vsCurrency stri
if common.IsSuitableTicker(ticker, vsCurrency, token) {
tickers[i] = ticker
prevTicker = ticker
+ prevTs = t
break
} else {
found = false
}
}
- if !found {
- tickers[i] = fr.currentTicker
- prevTicker = fr.currentTicker
- }
+ }
+ if !found {
+ tickers[i] = fr.currentTicker
+ prevTicker = fr.currentTicker
+ prevTs = t
}
}
}
@@ -266,22 +275,31 @@ func (fr *FiatRates) logTickersInfo() {
len(fr.fiveMinutesTickers), time.Unix(fr.fiveMinutesTickersFrom, 0).Format("2006-01-02 15:04"), time.Unix(fr.fiveMinutesTickersTo, 0).Format("2006-01-02 15:04"))
}
-func normalizedTimeUnix(t time.Time, granularity int64) int64 {
- return normalizedUnix(t.UTC().Unix(), granularity)
+func roundTimeUnix(t time.Time, granularity int64) int64 {
+ return roundUnix(t.UTC().Unix(), granularity)
}
-func normalizedUnix(t int64, granularity int64) int64 {
+func roundUnix(t int64, granularity int64) int64 {
unix := t + (granularity >> 1)
return unix - unix%granularity
}
+func ceilUnix(t int64, granularity int64) int64 {
+ unix := t + (granularity - 1)
+ return unix - unix%granularity
+}
+
// loadDailyTickers loads daily tickers to cache
func (fr *FiatRates) loadDailyTickers() error {
fr.mux.Lock()
defer fr.mux.Unlock()
fr.dailyTickers = make(map[int64]*common.CurrencyRatesTicker)
err := fr.db.FiatRatesGetAllTickers(func(ticker *common.CurrencyRatesTicker) error {
- normalizedTime := normalizedTimeUnix(ticker.Timestamp, secondsInDay)
+ normalizedTime := roundTimeUnix(ticker.Timestamp, secondsInDay)
+ if normalizedTime == fr.dailyTickersFrom {
+ // there are multiple tickers on the first day, use only the first one
+ return nil
+ }
// remove token rates from cache to save memory (tickers with token rates are hundreds of kb big)
ticker.TokenRates = nil
if len(fr.dailyTickers) > 0 {
@@ -321,8 +339,8 @@ func (fr *FiatRates) tickersToMap(tickers *[]common.CurrencyRatesTicker, granula
to := int64(0)
for i := range *tickers {
ticker := (*tickers)[i]
- normalizedTime := normalizedTimeUnix(ticker.Timestamp, granularitySeconds)
- dailyTime := normalizedTimeUnix(ticker.Timestamp, secondsInDay)
+ normalizedTime := roundTimeUnix(ticker.Timestamp, granularitySeconds)
+ dailyTime := roundTimeUnix(ticker.Timestamp, secondsInDay)
dailyTicker, found := fr.dailyTickers[dailyTime]
if !found {
// if not found in historical tickers, use current ticker
diff --git a/fiat/fiat_rates_test.go b/fiat/fiat_rates_test.go
index 868a25a2..13146114 100644
--- a/fiat/fiat_rates_test.go
+++ b/fiat/fiat_rates_test.go
@@ -132,18 +132,7 @@ func TestFiatRates(t *testing.T) {
// mocked CoinGecko API
configJSON := `{"fiat_rates": "coingecko", "fiat_rates_params": "{\"url\": \"` + mockServer.URL + `\", \"coin\": \"ethereum\",\"platformIdentifier\":\"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 60}"}`
- configFile, err := os.CreateTemp("", "config*.json")
- if err != nil {
- t.Fatalf("FiatRates configFile error: %v", err)
- }
- if _, err := configFile.WriteString(configJSON); err != nil {
- t.Fatalf("FiatRates configFile WriteString error: %v", err)
- }
- if err := configFile.Close(); err != nil {
- t.Fatalf("FiatRates configFile Close error: %v", err)
- }
-
- fiatRates, err := NewFiatRates(d, configFile.Name(), nil)
+ fiatRates, err := NewFiatRates(d, []byte(configJSON), nil)
if err != nil {
t.Fatalf("FiatRates init error: %v", err)
}
diff --git a/server/public.go b/server/public.go
index 45026053..5c199d7b 100644
--- a/server/public.go
+++ b/server/public.go
@@ -580,7 +580,11 @@ func (s *PublicServer) amountSpan(a *api.Amount, td *TemplateData, classes strin
if td.TxTicker == nil {
date := time.Unix(td.Tx.Blocktime, 0).UTC()
secondary := strings.ToLower(td.SecondaryCoin)
- ticker, _ := s.db.FiatRatesFindTicker(&date, secondary, "")
+ var ticker *common.CurrencyRatesTicker
+ tickers, err := s.fiatRates.GetTickersForTimestamps([]int64{int64(td.Tx.Blocktime)}, "", "")
+ if err == nil && tickers != nil && len(*tickers) > 0 {
+ ticker = (*tickers)[0]
+ }
if ticker != nil {
td.TxSecondaryCoinRate = float64(ticker.Rates[secondary])
// the ticker is from the midnight, valid for the whole day before
diff --git a/server/public_ethereumtype_test.go b/server/public_ethereumtype_test.go
index 26b6b49c..345be68f 100644
--- a/server/public_ethereumtype_test.go
+++ b/server/public_ethereumtype_test.go
@@ -7,11 +7,13 @@ import (
"net/http"
"net/http/httptest"
"testing"
+ "time"
"github.com/golang/glog"
"github.com/linxGnu/grocksdb"
"github.com/trezor/blockbook/bchain"
"github.com/trezor/blockbook/bchain/coins/eth"
+ "github.com/trezor/blockbook/common"
"github.com/trezor/blockbook/db"
"github.com/trezor/blockbook/tests/dbtestdata"
)
@@ -24,7 +26,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) {
status: http.StatusOK,
contentType: "text/html; charset=utf-8",
body: []string{
- `
Trezor Fake Coin ExplorerAddress address7b.eth
0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b
0.000000000123450123 FAKE
Confirmed | |
| Balance | 0.000000000123450123 FAKE |
| Transactions | 2 |
| Non-contract Transactions | 0 |
| Internal Transactions | 0 |
| Nonce | 123 |
Transactions
`,
+ `Trezor Fake Coin ExplorerAddress address7b.eth
0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b
0.000000000123450123 FAKE
0.00 USD
Confirmed | |
| Balance | 0.000000000123450123 FAKE0.00 USD |
| Transactions | 2 |
| Non-contract Transactions | 0 |
| Internal Transactions | 0 |
| Nonce | 123 |
Transactions
ERC20 Token Transfers
7.675000000000000001 S13-
7.674999999999991915 S13-
`,
},
},
{
@@ -33,7 +35,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) {
status: http.StatusOK,
contentType: "text/html; charset=utf-8",
body: []string{
- `Trezor Fake Coin ExplorerAddress
0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
0.000000000123450093 FAKE
Confirmed | |
| Balance | 0.000000000123450093 FAKE |
| Transactions | 1 |
| Non-contract Transactions | 1 |
| Internal Transactions | 0 |
| Nonce | 93 |
Transactions
0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
0 FAKE
ERC1155 Token Transfers
0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
`,
+ `Trezor Fake Coin ExplorerAddress
0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
0.000000000123450093 FAKE
0.00 USD
Confirmed | |
| Balance | 0.000000000123450093 FAKE0.00 USD |
| Transactions | 1 |
| Non-contract Transactions | 1 |
| Internal Transactions | 0 |
| Nonce | 93 |
Transactions
0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
0 FAKE0.00 USD0.00 USD
ERC1155 Token Transfers
0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
`,
},
},
{
@@ -42,14 +44,14 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) {
status: http.StatusOK,
contentType: "text/html; charset=utf-8",
body: []string{
- `Trezor Fake Coin ExplorerTransaction
0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101
| In Block | Unconfirmed |
| Status | Success |
| Value | 0 FAKE |
| Gas Used / Limit | 52025 / 78037 |
| Gas Price | 0.00000004 FAKE (40 Gwei) |
| Fees | 0.002081 FAKE |
| RBF | ON |
`,
+ `Trezor Fake Coin ExplorerTransaction
0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101
| In Block | Unconfirmed |
| Status | Success |
| Value | 0 FAKE0.00 USD0.00 USD |
| Gas Used / Limit | 52025 / 78037 |
| Gas Price | 0.00000004 FAKE0.00 USD0.00 USD (40 Gwei) |
| Fees | 0.002081 FAKE4.16 USD18.55 USD |
| RBF | ON |
`,
},
}, {
name: "explorerTokenDetail " + dbtestdata.EthAddr7b,
r: newGetRequest(ts.URL + "/nft/" + dbtestdata.EthAddrContractCd + "/" + "1"),
status: http.StatusOK,
contentType: "text/html; charset=utf-8",
- body: []string{`Trezor Fake Coin Explorer`},
+ body: []string{`Trezor Fake Coin Explorer`},
},
{
name: "apiIndex",
@@ -97,7 +99,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) {
status: http.StatusOK,
contentType: "application/json; charset=utf-8",
body: []string{
- `{"ts":1574344800,"rates":{"usd":7814.5}}`,
+ `{"ts":1574380800,"rates":{"usd":7914.5}}`,
},
},
{
@@ -106,16 +108,16 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) {
status: http.StatusOK,
contentType: "application/json; charset=utf-8",
body: []string{
- `{"ts":1574344800,"rates":{"usd":6251.6}}`,
+ `{"ts":1574380800,"rates":{"usd":1.2}}`,
},
},
{
name: "apiFiatRates get token rate by timestamp for all currencies",
r: newGetRequest(ts.URL + "/api/v2/tickers?timestamp=1574340000&token=0xA4DD6Bc15Be95Af55f0447555c8b6aA3088562f3"),
- status: http.StatusBadRequest,
+ status: http.StatusOK,
contentType: "application/json; charset=utf-8",
body: []string{
- `{"error":"Rates for token only for a single currency"}`,
+ `{"ts":1574380800,"rates":{"eur":1.0816754,"usd":1.2}}`,
},
},
{
@@ -147,7 +149,7 @@ func initEthereumTypeDB(d *db.RocksDB) error {
// initTestFiatRatesEthereumType initializes test data for /api/v2/tickers endpoint
func initTestFiatRatesEthereumType(d *db.RocksDB) error {
- if err := insertFiatRate("20180320020000", map[string]float32{
+ if err := insertFiatRate("20180320000000", map[string]float32{
"usd": 2000.0,
"eur": 1300.0,
}, map[string]float32{
@@ -156,7 +158,7 @@ func initTestFiatRatesEthereumType(d *db.RocksDB) error {
}, d); err != nil {
return err
}
- if err := insertFiatRate("20180320030000", map[string]float32{
+ if err := insertFiatRate("20180321000000", map[string]float32{
"usd": 2001.0,
"eur": 1301.0,
}, map[string]float32{
@@ -165,7 +167,7 @@ func initTestFiatRatesEthereumType(d *db.RocksDB) error {
}, d); err != nil {
return err
}
- if err := insertFiatRate("20180320040000", map[string]float32{
+ if err := insertFiatRate("20180322000000", map[string]float32{
"usd": 2002.0,
"eur": 1302.0,
}, map[string]float32{
@@ -174,7 +176,7 @@ func initTestFiatRatesEthereumType(d *db.RocksDB) error {
}, d); err != nil {
return err
}
- if err := insertFiatRate("20180321055521", map[string]float32{
+ if err := insertFiatRate("20180323000000", map[string]float32{
"usd": 2003.0,
"eur": 1303.0,
}, map[string]float32{
@@ -183,7 +185,7 @@ func initTestFiatRatesEthereumType(d *db.RocksDB) error {
}, d); err != nil {
return err
}
- if err := insertFiatRate("20191121140000", map[string]float32{
+ if err := insertFiatRate("20190321000000", map[string]float32{
"usd": 7814.5,
"eur": 7100.0,
}, map[string]float32{
@@ -193,14 +195,31 @@ func initTestFiatRatesEthereumType(d *db.RocksDB) error {
}, d); err != nil {
return err
}
- return insertFiatRate("20191121143015", map[string]float32{
+ if err := insertFiatRate("20191122000000", map[string]float32{
"usd": 7914.5,
"eur": 7134.1,
}, map[string]float32{
"0xdac17f958d2ee523a2206206994597c13d831ec7": 7914.1,
"0x2260fac5e5542a773aa44fbcfedf7c193bc2c599": 599.0,
"0xa4dd6bc15be95af55f0447555c8b6aa3088562f3": 1.2,
- }, d)
+ }, d); err != nil {
+ return err
+ }
+
+ return d.FiatRatesStoreSpecialTickers("CurrentTickers", &[]common.CurrencyRatesTicker{
+ {
+ Timestamp: time.Unix(1592821931, 0),
+ Rates: map[string]float32{
+ "usd": 8914.5,
+ "eur": 8134.1,
+ },
+ TokenRates: map[string]float32{
+ "0xdac17f958d2ee523a2206206994597c13d831ec7": 8914.1,
+ "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599": 899.0,
+ "0xa4dd6bc15be95af55f0447555c8b6aa3088562f3": 8.2,
+ },
+ },
+ })
}
func Test_PublicServer_EthereumType(t *testing.T) {
diff --git a/server/public_test.go b/server/public_test.go
index 8f01c468..49eb3630 100644
--- a/server/public_test.go
+++ b/server/public_test.go
@@ -121,8 +121,15 @@ func setupPublicHTTPServer(parser bchain.BlockChainParser, chain bchain.BlockCha
glog.Fatal("txCache: ", err)
}
+ // mocked CoinGecko API
+ configJSON := `{"fiat_rates": "coingecko", "fiat_rates_params": "{\"url\": \"none\", \"coin\": \"ethereum\",\"platformIdentifier\":\"ethereum\",\"platformVsCurrency\": \"usd\",\"periodSeconds\": 60}"}`
+ fiatRates, err := fiat.NewFiatRates(d, []byte(configJSON), nil)
+ if err != nil {
+ glog.Fatal("fiatRates ", err)
+ }
+
// s.Run is never called, binding can be to any port
- s, err := NewPublicServer("localhost:12345", "", d, chain, mempool, txCache, "", metrics, is, &fiat.FiatRates{}, false)
+ s, err := NewPublicServer("localhost:12345", "", d, chain, mempool, txCache, "", metrics, is, fiatRates, false)
if err != nil {
t.Fatal(err)
}
@@ -187,37 +194,37 @@ func insertFiatRate(date string, rates map[string]float32, tokenRates map[string
// initTestFiatRates initializes test data for /api/v2/tickers endpoint
func initTestFiatRates(d *db.RocksDB) error {
- if err := insertFiatRate("20180320020000", map[string]float32{
+ if err := insertFiatRate("20180320000000", map[string]float32{
"usd": 2000.0,
"eur": 1300.0,
}, nil, d); err != nil {
return err
}
- if err := insertFiatRate("20180320030000", map[string]float32{
+ if err := insertFiatRate("20180321000000", map[string]float32{
"usd": 2001.0,
"eur": 1301.0,
}, nil, d); err != nil {
return err
}
- if err := insertFiatRate("20180320040000", map[string]float32{
+ if err := insertFiatRate("20180322000000", map[string]float32{
"usd": 2002.0,
"eur": 1302.0,
}, nil, d); err != nil {
return err
}
- if err := insertFiatRate("20180321055521", map[string]float32{
+ if err := insertFiatRate("20180324000000", map[string]float32{
"usd": 2003.0,
"eur": 1303.0,
}, nil, d); err != nil {
return err
}
- if err := insertFiatRate("20191121140000", map[string]float32{
+ if err := insertFiatRate("20191121000000", map[string]float32{
"usd": 7814.5,
"eur": 7100.0,
}, nil, d); err != nil {
return err
}
- return insertFiatRate("20191121143015", map[string]float32{
+ return insertFiatRate("20191122000000", map[string]float32{
"usd": 7914.5,
"eur": 7134.1,
}, nil, d)
@@ -323,7 +330,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
contentType: "text/html; charset=utf-8",
body: []string{
`Trezor Fake Coin ExplorerApplication status
Synchronization with backend is disabled, the state of index is not up to date.
Blockbook | |
| Coin | Fakecoin |
| Host | |
| Version / Commit / Build | unknown / unknown / unknown |
| Synchronized | true |
| Last Block | 225494 |
| Last Block Update | `,
- ` |
| Mempool in Sync | false |
| Last Mempool Update | |
| Transactions in Mempool | 0 |
| Size On Disk | `,
+ `
| Mempool in Sync | false |
| Last Mempool Update | |
| Transactions in Mempool | 0 |
| Current Fiat rates | `,
`
Backend | |
| Chain | fakecoin |
| Version | 001001 |
| Subversion | /Fakecoin:0.0.1/ |
| Last Block | 2 |
| Difficulty | |
Blockbook - blockchain indexer for Trezor Suite https://trezor.io/trezor-suite. Do not use for any other purpose. `,
},
},
@@ -485,12 +492,12 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
},
},
{
- name: "apiFiatRates missing currency",
+ name: "apiFiatRates all currencies",
r: newGetRequest(ts.URL + "/api/v2/tickers"),
status: http.StatusOK,
contentType: "application/json; charset=utf-8",
body: []string{
- `{"ts":1574346615,"rates":{"eur":7134.1,"usd":7914.5}}`,
+ `{"ts":1574380800,"rates":{"eur":7134.1,"usd":7914.5}}`,
},
},
{
@@ -499,16 +506,16 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
status: http.StatusOK,
contentType: "application/json; charset=utf-8",
body: []string{
- `{"ts":1574346615,"rates":{"usd":7914.5}}`,
+ `{"ts":1574380800,"rates":{"usd":7914.5}}`,
},
},
{
name: "apiFiatRates get rate by exact timestamp",
- r: newGetRequest(ts.URL + "/api/v2/tickers?currency=usd×tamp=1574344800"),
+ r: newGetRequest(ts.URL + "/api/v2/tickers?currency=usd×tamp=1521545531"),
status: http.StatusOK,
contentType: "application/json; charset=utf-8",
body: []string{
- `{"ts":1574344800,"rates":{"usd":7814.5}}`,
+ `{"ts":1521590400,"rates":{"usd":2001}}`,
},
},
{
@@ -544,25 +551,25 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
status: http.StatusOK,
contentType: "application/json; charset=utf-8",
body: []string{
- `{"ts":1574344800,"rates":{"eur":7100}}`,
+ `{"ts":1574380800,"rates":{"eur":7134.1}`,
},
},
{
name: "apiMultiFiatRates all currencies",
- r: newGetRequest(ts.URL + "/api/v2/multi-tickers?timestamp=1574344800,1574346615"),
+ r: newGetRequest(ts.URL + "/api/v2/multi-tickers?timestamp=1574344800,1521677000"),
status: http.StatusOK,
contentType: "application/json; charset=utf-8",
body: []string{
- `[{"ts":1574344800,"rates":{"eur":7100,"usd":7814.5}},{"ts":1574346615,"rates":{"eur":7134.1,"usd":7914.5}}]`,
+ `[{"ts":1574380800,"rates":{"eur":7134.1,"usd":7914.5}},{"ts":1521849600,"rates":{"eur":1303,"usd":2003}}]`,
},
},
{
name: "apiMultiFiatRates get EUR rate",
- r: newGetRequest(ts.URL + "/api/v2/multi-tickers?timestamp=1574344800,1574346615¤cy=eur"),
+ r: newGetRequest(ts.URL + "/api/v2/multi-tickers?timestamp=1521545531,1574346615¤cy=eur"),
status: http.StatusOK,
contentType: "application/json; charset=utf-8",
body: []string{
- `[{"ts":1574344800,"rates":{"eur":7100}},{"ts":1574346615,"rates":{"eur":7134.1}}]`,
+ `[{"ts":1521590400,"rates":{"eur":1301}},{"ts":1574380800,"rates":{"eur":7134.1}}]`,
},
},
{
@@ -571,7 +578,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
status: http.StatusOK,
contentType: "application/json; charset=utf-8",
body: []string{
- `{"ts":1521511200,"rates":{"usd":2000}}`,
+ `{"ts":1521504000,"rates":{"usd":2000}}`,
},
},
{
@@ -580,7 +587,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
status: http.StatusOK,
contentType: "application/json; charset=utf-8",
body: []string{
- `{"ts":1521611721,"rates":{"usd":2003}}`,
+ `{"ts":1521676800,"rates":{"usd":2002}}`,
},
},
{
@@ -589,7 +596,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
status: http.StatusOK,
contentType: "application/json; charset=utf-8",
body: []string{
- `{"ts":1574346615,"rates":{"eur":7134.1}}`,
+ `{"ts":1574380800,"rates":{"eur":7134.1}}`,
},
},
{
@@ -607,7 +614,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
status: http.StatusOK,
contentType: "application/json; charset=utf-8",
body: []string{
- `{"ts":1574346615,"available_currencies":["eur","usd"]}`,
+ `{"ts":1574380800,"available_currencies":["eur","usd"]}`,
},
},
{
@@ -778,7 +785,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
status: http.StatusOK,
contentType: "application/json; charset=utf-8",
body: []string{
- `[{"time":1521514800,"txs":1,"received":"24690","sent":"0","sentToSelf":"0","rates":{"eur":1301,"usd":2001}},{"time":1521594000,"txs":1,"received":"0","sent":"12345","sentToSelf":"0","rates":{"eur":1303,"usd":2003}}]`,
+ `[{"time":1521514800,"txs":1,"received":"24690","sent":"0","sentToSelf":"0","rates":{"eur":1301,"usd":2001}},{"time":1521594000,"txs":1,"received":"0","sent":"12345","sentToSelf":"0","rates":{"eur":1302,"usd":2002}}]`,
},
},
{
@@ -787,7 +794,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
status: http.StatusOK,
contentType: "application/json; charset=utf-8",
body: []string{
- `[{"time":1521514800,"txs":1,"received":"9876","sent":"0","sentToSelf":"0","rates":{"eur":1301,"usd":2001}},{"time":1521594000,"txs":1,"received":"9000","sent":"9876","sentToSelf":"9000","rates":{"eur":1303,"usd":2003}}]`,
+ `[{"time":1521514800,"txs":1,"received":"9876","sent":"0","sentToSelf":"0","rates":{"eur":1301,"usd":2001}},{"time":1521594000,"txs":1,"received":"9000","sent":"9876","sentToSelf":"9000","rates":{"eur":1302,"usd":2002}}]`,
},
},
{
@@ -796,7 +803,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
status: http.StatusOK,
contentType: "application/json; charset=utf-8",
body: []string{
- `[{"time":1521514800,"txs":1,"received":"9876","sent":"0","sentToSelf":"0","rates":{"eur":1301}},{"time":1521594000,"txs":1,"received":"9000","sent":"9876","sentToSelf":"9000","rates":{"eur":1303}}]`,
+ `[{"time":1521514800,"txs":1,"received":"9876","sent":"0","sentToSelf":"0","rates":{"eur":1301}},{"time":1521594000,"txs":1,"received":"9000","sent":"9876","sentToSelf":"9000","rates":{"eur":1302}}]`,
},
},
{
@@ -814,7 +821,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
status: http.StatusOK,
contentType: "application/json; charset=utf-8",
body: []string{
- `[{"time":1521514800,"txs":1,"received":"1","sent":"0","sentToSelf":"0","rates":{"eur":1301,"usd":2001}},{"time":1521594000,"txs":1,"received":"118641975500","sent":"1","sentToSelf":"118641975500","rates":{"eur":1303,"usd":2003}}]`,
+ `[{"time":1521514800,"txs":1,"received":"1","sent":"0","sentToSelf":"0","rates":{"eur":1301,"usd":2001}},{"time":1521594000,"txs":1,"received":"118641975500","sent":"1","sentToSelf":"118641975500","rates":{"eur":1302,"usd":2002}}]`,
},
},
{
@@ -841,7 +848,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) {
status: http.StatusOK,
contentType: "application/json; charset=utf-8",
body: []string{
- `[{"time":1521594000,"txs":1,"received":"118641975500","sent":"1","sentToSelf":"118641975500","rates":{"eur":1303,"usd":2003}}]`,
+ `[{"time":1521594000,"txs":1,"received":"118641975500","sent":"1","sentToSelf":"118641975500","rates":{"eur":1302,"usd":2002}}]`,
},
},
{
@@ -1210,7 +1217,7 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) {
"currencies": []string{},
},
},
- want: `{"id":"18","data":{"ts":1574346615,"rates":{"eur":7134.1,"usd":7914.5}}}`,
+ want: `{"id":"18","data":{"ts":1574380800,"rates":{"eur":7134.1,"usd":7914.5}}}`,
},
{
name: "websocket getCurrentFiatRates usd",
@@ -1220,7 +1227,7 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) {
"currencies": []string{"usd"},
},
},
- want: `{"id":"19","data":{"ts":1574346615,"rates":{"usd":7914.5}}}`,
+ want: `{"id":"19","data":{"ts":1574380800,"rates":{"usd":7914.5}}}`,
},
{
name: "websocket getCurrentFiatRates eur",
@@ -1230,7 +1237,7 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) {
"currencies": []string{"eur"},
},
},
- want: `{"id":"20","data":{"ts":1574346615,"rates":{"eur":7134.1}}}`,
+ want: `{"id":"20","data":{"ts":1574380800,"rates":{"eur":7134.1}}}`,
},
{
name: "websocket getCurrentFiatRates incorrect currency",
@@ -1291,10 +1298,10 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) {
Method: "getFiatRatesForTimestamps",
Params: map[string]interface{}{
"currencies": []string{"usd"},
- "timestamps": []int64{1574346615},
+ "timestamps": []int64{1574380800},
},
},
- want: `{"id":"26","data":{"tickers":[{"ts":1574346615,"rates":{"usd":7914.5}}]}}`,
+ want: `{"id":"26","data":{"tickers":[{"ts":1574380800,"rates":{"usd":7914.5}}]}}`,
},
{
name: "websocket getFiatRatesForTimestamps closest date, eur",
@@ -1305,7 +1312,7 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) {
"timestamps": []int64{1521507600},
},
},
- want: `{"id":"27","data":{"tickers":[{"ts":1521511200,"rates":{"eur":1300}}]}}`,
+ want: `{"id":"27","data":{"tickers":[{"ts":1521590400,"rates":{"eur":1301}}]}}`,
},
{
name: "websocket getFiatRatesForTimestamps multiple timestamps usd",
@@ -1316,7 +1323,7 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) {
"timestamps": []int64{1570346615, 1574346615},
},
},
- want: `{"id":"28","data":{"tickers":[{"ts":1574344800,"rates":{"usd":7814.5}},{"ts":1574346615,"rates":{"usd":7914.5}}]}}`,
+ want: `{"id":"28","data":{"tickers":[{"ts":1574294400,"rates":{"usd":7814.5}},{"ts":1574380800,"rates":{"usd":7914.5}}]}}`,
},
{
name: "websocket getFiatRatesForTimestamps multiple timestamps eur",
@@ -1327,7 +1334,7 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) {
"timestamps": []int64{1570346615, 1574346615},
},
},
- want: `{"id":"29","data":{"tickers":[{"ts":1574344800,"rates":{"eur":7100}},{"ts":1574346615,"rates":{"eur":7134.1}}]}}`,
+ want: `{"id":"29","data":{"tickers":[{"ts":1574294400,"rates":{"eur":7100}},{"ts":1574380800,"rates":{"eur":7134.1}}]}}`,
},
{
name: "websocket getFiatRatesForTimestamps multiple timestamps with an error",
@@ -1338,7 +1345,7 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) {
"timestamps": []int64{1570346615, 1574346615, 2000000000},
},
},
- want: `{"id":"30","data":{"tickers":[{"ts":1574344800,"rates":{"usd":7814.5}},{"ts":1574346615,"rates":{"usd":7914.5}},{"ts":2000000000,"rates":{"usd":-1}}]}}`,
+ want: `{"id":"30","data":{"tickers":[{"ts":1574294400,"rates":{"usd":7814.5}},{"ts":1574380800,"rates":{"usd":7914.5}},{"ts":2000000000,"rates":{"usd":-1}}]}}`,
},
{
name: "websocket getFiatRatesForTimestamps multiple errors",
@@ -1359,7 +1366,7 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) {
"timestamp": 1570346615,
},
},
- want: `{"id":"32","data":{"ts":1574344800,"available_currencies":["eur","usd"]}}`,
+ want: `{"id":"32","data":{"ts":1574294400,"available_currencies":["eur","usd"]}}`,
},
{
name: "websocket getBalanceHistory Addr2",
@@ -1369,7 +1376,7 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) {
"descriptor": "mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz",
},
},
- want: `{"id":"33","data":[{"time":1521514800,"txs":1,"received":"24690","sent":"0","sentToSelf":"0","rates":{"eur":1301,"usd":2001}},{"time":1521594000,"txs":1,"received":"0","sent":"12345","sentToSelf":"0","rates":{"eur":1303,"usd":2003}}]}`,
+ want: `{"id":"33","data":[{"time":1521514800,"txs":1,"received":"24690","sent":"0","sentToSelf":"0","rates":{"eur":1301,"usd":2001}},{"time":1521594000,"txs":1,"received":"0","sent":"12345","sentToSelf":"0","rates":{"eur":1302,"usd":2002}}]}`,
},
{
name: "websocket getBalanceHistory xpub",
@@ -1379,7 +1386,7 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) {
"descriptor": dbtestdata.Xpub,
},
},
- want: `{"id":"34","data":[{"time":1521514800,"txs":1,"received":"1","sent":"0","sentToSelf":"0","rates":{"eur":1301,"usd":2001}},{"time":1521594000,"txs":1,"received":"118641975500","sent":"1","sentToSelf":"118641975500","rates":{"eur":1303,"usd":2003}}]}`,
+ want: `{"id":"34","data":[{"time":1521514800,"txs":1,"received":"1","sent":"0","sentToSelf":"0","rates":{"eur":1301,"usd":2001}},{"time":1521594000,"txs":1,"received":"118641975500","sent":"1","sentToSelf":"118641975500","rates":{"eur":1302,"usd":2002}}]}`,
},
{
name: "websocket getBalanceHistory xpub from=1521504000&to=1521590400 currencies=[usd]",
diff --git a/static/templates/base.html b/static/templates/base.html
index 86f2631e..1323c2be 100644
--- a/static/templates/base.html
+++ b/static/templates/base.html
@@ -53,7 +53,7 @@
{{range $c := .SecondaryCurrencies}}
{{end}}
-
+
{{end}}