diff --git a/api/types.go b/api/types.go index 060b550f..e15d0536 100644 --- a/api/types.go +++ b/api/types.go @@ -5,6 +5,7 @@ import ( "errors" "math/big" "sort" + "strings" "time" "github.com/trezor/blockbook/bchain" @@ -64,6 +65,21 @@ func IsZeroBigInt(b *big.Int) bool { return len(b.Bits()) == 0 } +// Compare returns an integer comparing two Amounts. The result will be 0 if a == b, -1 if a < b, and +1 if a > b. +// Nil Amount is always less then non nil amount, two nil Amounts are equal +func (a *Amount) Compare(b *Amount) int { + if b == nil { + if a == nil { + return 0 + } + return 1 + } + if a == nil { + return -1 + } + return (*big.Int)(a).Cmp((*big.Int)(b)) +} + // MarshalJSON Amount serialization func (a *Amount) MarshalJSON() (out []byte, err error) { if a == nil { @@ -151,6 +167,8 @@ type Token struct { Symbol string `json:"symbol,omitempty"` Decimals int `json:"decimals,omitempty"` BalanceSat *Amount `json:"balance,omitempty"` + BaseValue float64 `json:"baseValue,omitempty"` + FiatValue float64 `json:"fiatValue,omitempty"` Ids []Amount `json:"ids,omitempty"` // multiple ERC721 tokens MultiTokenValues []MultiTokenValue `json:"multiTokenValues,omitempty"` // multiple ERC1155 tokens TotalReceivedSat *Amount `json:"totalReceived,omitempty"` @@ -158,6 +176,30 @@ type Token struct { ContractIndex string `json:"-"` } +// Tokens is array of Token +type Tokens []Token + +func (a Tokens) Len() int { return len(a) } +func (a Tokens) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a Tokens) Less(i, j int) bool { + ti := &a[i] + tj := &a[j] + // sort by BaseValue descending and then Name and then by Contract + if ti.BaseValue < tj.BaseValue { + return false + } else if ti.BaseValue > tj.BaseValue { + return true + } + c := strings.Compare(ti.Name, tj.Name) + if c == 1 { + return false + } else if c == -1 { + return true + } + c = strings.Compare(ti.Contract, tj.Contract) + return c == -1 +} + // TokenTransfer contains info about a token transfer done in a transaction type TokenTransfer struct { Type bchain.TokenTypeName `json:"type"` @@ -286,7 +328,11 @@ type Address struct { Txids []string `json:"txids,omitempty"` Nonce string `json:"nonce,omitempty"` UsedTokens int `json:"usedTokens,omitempty"` - Tokens []Token `json:"tokens,omitempty"` + Tokens Tokens `json:"tokens,omitempty"` + TokensBaseValue float64 `json:"tokensBaseValue,omitempty"` + TokensFiatValue float64 `json:"tokensFiatValue,omitempty"` + TotalBaseValue float64 `json:"totalBaseValue,omitempty"` + TotalFiatValue float64 `json:"totalFiatValue,omitempty"` ContractInfo *bchain.ContractInfo `json:"contractInfo,omitempty"` AddressAliases AddressAliasesMap `json:"addressAliases,omitempty"` // helpers for explorer diff --git a/api/types_test.go b/api/types_test.go index fcf8a76a..07b3a54b 100644 --- a/api/types_test.go +++ b/api/types_test.go @@ -172,3 +172,50 @@ func TestBalanceHistories_SortAndAggregate(t *testing.T) { }) } } + +func TestAmount_Compare(t *testing.T) { + tests := []struct { + name string + a *Amount + b *Amount + want int + }{ + { + name: "nil-nil", + a: nil, + b: nil, + want: 0, + }, + { + name: "20-nil", + a: (*Amount)(big.NewInt(20)), + b: nil, + want: 1, + }, + { + name: "nil-20", + a: nil, + b: (*Amount)(big.NewInt(20)), + want: -1, + }, + { + name: "18-20", + a: (*Amount)(big.NewInt(18)), + b: (*Amount)(big.NewInt(20)), + want: -1, + }, + { + name: "20-20", + a: (*Amount)(big.NewInt(20)), + b: (*Amount)(big.NewInt(20)), + want: 0, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.a.Compare(tt.b); got != tt.want { + t.Errorf("Amount.Compare() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/api/worker.go b/api/worker.go index 4eb0435a..60675692 100644 --- a/api/worker.go +++ b/api/worker.go @@ -833,7 +833,7 @@ func computePaging(count, page, itemsOnPage int) (Paging, int, int, int) { }, from, to, page } -func (w *Worker) getEthereumContractBalance(addrDesc bchain.AddressDescriptor, index int, c *db.AddrContract, details AccountDetails) (*Token, error) { +func (w *Worker) getEthereumContractBalance(addrDesc bchain.AddressDescriptor, index int, c *db.AddrContract, details AccountDetails, ticker *common.CurrencyRatesTicker, secondaryCoin string) (*Token, error) { typeName := bchain.EthereumTokenTypeMap[c.Type] ci, validContract, err := w.getContractDescriptorInfo(c.Contract, typeName) if err != nil { @@ -858,6 +858,21 @@ func (w *Worker) getEthereumContractBalance(addrDesc bchain.AddressDescriptor, i glog.Warningf("EthereumTypeGetErc20ContractBalance addr %v, contract %v, %v", addrDesc, c.Contract, err) } else { t.BalanceSat = (*Amount)(b) + if secondaryCoin != "" { + baseRate, found := w.GetContractBaseRate(ticker, t.Contract, 0) + if found { + value, err := strconv.ParseFloat(t.BalanceSat.DecimalString(t.Decimals), 64) + if err == nil { + t.BaseValue = value * baseRate + if ticker != nil { + secondaryRate, found := ticker.Rates[secondaryCoin] + if found { + t.FiatValue = t.BaseValue * float64(secondaryRate) + } + } + } + } + } } } else { if len(c.Ids) > 0 { @@ -910,30 +925,57 @@ func (w *Worker) getEthereumContractBalanceFromBlockchain(addrDesc, contract bch }, nil } -func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescriptor, details AccountDetails, filter *AddressFilter) (*db.AddrBalance, []Token, *bchain.ContractInfo, uint64, int, int, int, error) { - var ( - ba *db.AddrBalance - tokens []Token - ci *bchain.ContractInfo - n uint64 - nonContractTxs int - internalTxs int - ) - // unknown number of results for paging - totalResults := -1 +// 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) { + if ticker == nil { + return 0, false + } + rate, found := ticker.GetTokenRate(contract) + if !found { + var date time.Time + if blocktime == 0 { + date = time.Now().UTC() + } else { + date = time.Unix(blocktime, 0).UTC() + } + ticker, _ = w.db.FiatRatesFindTicker(&date, "", contract) + if ticker == nil { + return 0, false + } + rate, found = ticker.GetTokenRate(contract) + } + + return float64(rate), found +} + +type ethereumTypeAddressData struct { + tokens Tokens + contractInfo *bchain.ContractInfo + nonce string + nonContractTxs int + internalTxs int + totalResults int + tokensBaseValue float64 + tokensFiatValue float64 +} + +func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescriptor, details AccountDetails, filter *AddressFilter, secondaryCoin string) (*db.AddrBalance, *ethereumTypeAddressData, error) { + var ba *db.AddrBalance + // unknown number of results for paging initially + d := ethereumTypeAddressData{totalResults: -1} ca, err := w.db.GetAddrDescContracts(addrDesc) if err != nil { - return nil, nil, nil, 0, 0, 0, 0, NewAPIError(fmt.Sprintf("Address not found, %v", err), true) + return nil, nil, NewAPIError(fmt.Sprintf("Address not found, %v", err), true) } b, err := w.chain.EthereumTypeGetBalance(addrDesc) if err != nil { - return nil, nil, nil, 0, 0, 0, 0, errors.Annotatef(err, "EthereumTypeGetBalance %v", addrDesc) + return nil, nil, errors.Annotatef(err, "EthereumTypeGetBalance %v", addrDesc) } var filterDesc bchain.AddressDescriptor if filter.Contract != "" { filterDesc, err = w.chainParser.GetAddrDescFromAddress(filter.Contract) if err != nil { - return nil, nil, nil, 0, 0, 0, 0, NewAPIError(fmt.Sprintf("Invalid contract filter, %v", err), true) + return nil, nil, NewAPIError(fmt.Sprintf("Invalid contract filter, %v", err), true) } } if ca != nil { @@ -943,12 +985,14 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto if b != nil { ba.BalanceSat = *b } - n, err = w.chain.EthereumTypeGetNonce(addrDesc) + n, err := w.chain.EthereumTypeGetNonce(addrDesc) if err != nil { - return nil, nil, nil, 0, 0, 0, 0, errors.Annotatef(err, "EthereumTypeGetNonce %v", addrDesc) + return nil, nil, errors.Annotatef(err, "EthereumTypeGetNonce %v", addrDesc) } + d.nonce = strconv.Itoa(int(n)) + ticker := w.is.GetCurrentTicker("", "") if details > AccountDetailsBasic { - tokens = make([]Token, len(ca.Contracts)) + d.tokens = make([]Token, len(ca.Contracts)) var j int for i := range ca.Contracts { c := &ca.Contracts[i] @@ -959,35 +1003,38 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto // filter only transactions of this contract filter.Vout = i + db.ContractIndexOffset } - t, err := w.getEthereumContractBalance(addrDesc, i+db.ContractIndexOffset, c, details) + t, err := w.getEthereumContractBalance(addrDesc, i+db.ContractIndexOffset, c, details, ticker, secondaryCoin) if err != nil { - return nil, nil, nil, 0, 0, 0, 0, err + return nil, nil, err } - tokens[j] = *t + d.tokens[j] = *t + d.tokensBaseValue += t.BaseValue + d.tokensFiatValue += t.FiatValue j++ } - tokens = tokens[:j] + d.tokens = d.tokens[:j] + sort.Sort(d.tokens) } - ci, err = w.db.GetContractInfo(addrDesc, "") + d.contractInfo, err = w.db.GetContractInfo(addrDesc, "") if err != nil { - return nil, nil, nil, 0, 0, 0, 0, err + return nil, nil, err } if filter.FromHeight == 0 && filter.ToHeight == 0 { // compute total results for paging if filter.Vout == AddressFilterVoutOff { - totalResults = int(ca.TotalTxs) + d.totalResults = int(ca.TotalTxs) } else if filter.Vout == 0 { - totalResults = int(ca.NonContractTxs) + d.totalResults = int(ca.NonContractTxs) } else if filter.Vout == db.InternalTxIndexOffset { - totalResults = int(ca.InternalTxs) + d.totalResults = int(ca.InternalTxs) } else if filter.Vout >= db.ContractIndexOffset && filter.Vout-db.ContractIndexOffset < len(ca.Contracts) { - totalResults = int(ca.Contracts[filter.Vout-db.ContractIndexOffset].Txs) + d.totalResults = int(ca.Contracts[filter.Vout-db.ContractIndexOffset].Txs) } else if filter.Vout == AddressFilterVoutQueryNotNecessary { - totalResults = 0 + d.totalResults = 0 } } - nonContractTxs = int(ca.NonContractTxs) - internalTxs = int(ca.InternalTxs) + d.nonContractTxs = int(ca.NonContractTxs) + d.internalTxs = int(ca.InternalTxs) } else { // addresses without any normal transactions can have internal transactions that were not processed and therefore balance if b != nil { @@ -997,20 +1044,20 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto } } // special handling if filtering for a contract, return the contract details even though the address had no transactions with it - if len(tokens) == 0 && len(filterDesc) > 0 && details >= AccountDetailsTokens { + if len(d.tokens) == 0 && len(filterDesc) > 0 && details >= AccountDetailsTokens { t, err := w.getEthereumContractBalanceFromBlockchain(addrDesc, filterDesc, details) if err != nil { - return nil, nil, nil, 0, 0, 0, 0, err + return nil, nil, err } - tokens = []Token{*t} + d.tokens = []Token{*t} // switch off query for transactions, there are no transactions filter.Vout = AddressFilterVoutQueryNotNecessary - totalResults = -1 + d.totalResults = -1 } - return ba, tokens, ci, n, nonContractTxs, internalTxs, totalResults, nil + return ba, &d, nil } -func (w *Worker) txFromTxid(txid string, bestheight uint32, option AccountDetails, blockInfo *db.BlockInfo, addresses map[string]struct{}) (*Tx, error) { +func (w *Worker) txFromTxid(txid string, bestHeight uint32, option AccountDetails, blockInfo *db.BlockInfo, addresses map[string]struct{}) (*Tx, error) { var tx *Tx var err error // only ChainBitcoinType supports TxHistoryLight @@ -1038,7 +1085,7 @@ func (w *Worker) txFromTxid(txid string, bestheight uint32, option AccountDetail blockInfo = &db.BlockInfo{} } } - tx = w.txFromTxAddress(txid, ta, blockInfo, bestheight, addresses) + tx = w.txFromTxAddress(txid, ta, blockInfo, bestHeight, addresses) } } else { tx, err = w.getTransaction(txid, false, false, addresses) @@ -1093,7 +1140,7 @@ func setIsOwnAddress(tx *Tx, address string) { } // GetAddress computes address value and gets transactions for given address -func (w *Worker) GetAddress(address string, page int, txsOnPage int, option AccountDetails, filter *AddressFilter) (*Address, error) { +func (w *Worker) GetAddress(address string, page int, txsOnPage int, option AccountDetails, filter *AddressFilter, secondaryCoin string) (*Address, error) { start := time.Now() page-- if page < 0 { @@ -1101,31 +1148,26 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco } var ( ba *db.AddrBalance - tokens []Token - contractInfo *bchain.ContractInfo txm []string txs []*Tx txids []string pg Paging uBalSat big.Int totalReceived, totalSent *big.Int - nonce string unconfirmedTxs int - nonTokenTxs int - internalTxs int totalResults int ) + ed := ðereumTypeAddressData{} addrDesc, address, err := w.getAddrDescAndNormalizeAddress(address) if err != nil { return nil, err } if w.chainType == bchain.ChainEthereumType { - var n uint64 - ba, tokens, contractInfo, n, nonTokenTxs, internalTxs, totalResults, err = w.getEthereumTypeAddressBalances(addrDesc, option, filter) + ba, ed, err = w.getEthereumTypeAddressBalances(addrDesc, option, filter, secondaryCoin) if err != nil { return nil, err } - nonce = strconv.Itoa(int(n)) + totalResults = ed.totalResults } else { // ba can be nil if the address is only in mempool! ba, err = w.db.GetAddrDescBalance(addrDesc, db.AddressBalanceDetailNoUTXO) @@ -1214,10 +1256,22 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco } } } + var secondaryRate, totalFiatValue float64 + ticker := w.is.GetCurrentTicker("", "") + totalBaseValue, err := strconv.ParseFloat((*Amount)(&ba.BalanceSat).DecimalString(w.chainParser.AmountDecimals()), 64) + if ticker != nil && err == nil { + r, found := ticker.Rates[secondaryCoin] + if found { + secondaryRate = float64(r) + } + } if w.chainType == bchain.ChainBitcoinType { totalReceived = ba.ReceivedSat() totalSent = &ba.SentSat + } else { + totalBaseValue += ed.tokensBaseValue } + totalFiatValue = secondaryRate * totalBaseValue r := &Address{ Paging: pg, AddrStr: address, @@ -1225,15 +1279,19 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco TotalReceivedSat: (*Amount)(totalReceived), TotalSentSat: (*Amount)(totalSent), Txs: int(ba.Txs), - NonTokenTxs: nonTokenTxs, - InternalTxs: internalTxs, + NonTokenTxs: ed.nonContractTxs, + InternalTxs: ed.internalTxs, UnconfirmedBalanceSat: (*Amount)(&uBalSat), UnconfirmedTxs: unconfirmedTxs, Transactions: txs, Txids: txids, - Tokens: tokens, - ContractInfo: contractInfo, - Nonce: nonce, + Tokens: ed.tokens, + TokensBaseValue: ed.tokensBaseValue, + TokensFiatValue: ed.tokensFiatValue, + TotalBaseValue: totalBaseValue, + TotalFiatValue: totalFiatValue, + ContractInfo: ed.contractInfo, + Nonce: ed.nonce, AddressAliases: w.getAddressAliases(addresses), } glog.Info("GetAddress ", address, ", ", time.Since(start)) diff --git a/common/currencyrateticker.go b/common/currencyrateticker.go index d6bcce5f..dae0f1b2 100644 --- a/common/currencyrateticker.go +++ b/common/currencyrateticker.go @@ -1,6 +1,9 @@ package common -import "time" +import ( + "strings" + "time" +) // CurrencyRatesTicker contains coin ticker data fetched from API type CurrencyRatesTicker struct { @@ -9,6 +12,15 @@ type CurrencyRatesTicker struct { TokenRates map[string]float32 `json:"tokenRates"` // rates of the tokens (identified by the address of the contract) against the base currency } +// Convert returns token rate in base currency +func (t *CurrencyRatesTicker) GetTokenRate(token string) (float32, bool) { + if t.TokenRates != nil { + rate, found := t.TokenRates[strings.ToLower(token)] + return rate, found + } + return 0, false +} + // Convert converts value in base currency to toCurrency func (t *CurrencyRatesTicker) Convert(baseValue float64, toCurrency string) float64 { rate, found := t.Rates[toCurrency] @@ -20,11 +32,9 @@ func (t *CurrencyRatesTicker) Convert(baseValue float64, toCurrency string) floa // ConvertTokenToBase converts token value to base currency func (t *CurrencyRatesTicker) ConvertTokenToBase(value float64, token string) float64 { - if t.TokenRates != nil { - rate, found := t.TokenRates[token] - if found { - return value * float64(rate) - } + rate, found := t.GetTokenRate(token) + if found { + return value * float64(rate) } return 0 } @@ -40,13 +50,11 @@ func (t *CurrencyRatesTicker) ConvertToken(value float64, token string, toCurren // TokenRateInCurrency return token rate in toCurrency currency func (t *CurrencyRatesTicker) TokenRateInCurrency(token string, toCurrency string) float32 { - if t.TokenRates != nil { - rate, found := t.TokenRates[token] + rate, found := t.GetTokenRate(token) + if found { + baseRate, found := t.Rates[toCurrency] if found { - baseRate, found := t.Rates[toCurrency] - if found { - return baseRate * rate - } + return baseRate * rate } } return 0 diff --git a/server/public.go b/server/public.go index 6b2688e2..019f65d0 100644 --- a/server/public.go +++ b/server/public.go @@ -501,7 +501,6 @@ type TemplateData struct { UseSecondaryCoin bool CurrentSecondaryCoinRate float64 CurrentTicker *common.CurrencyRatesTicker - TxBlocktime int64 TxDate string TxSecondaryCoinRate float64 TxTicker *common.CurrencyRatesTicker @@ -514,6 +513,9 @@ func (s *PublicServer) parseTemplates() []*template.Template { "amountSpan": s.amountSpan, "tokenAmountSpan": s.tokenAmountSpan, "amountSatsSpan": s.amountSatsSpan, + "formattedAmountSpan": s.formattedAmountSpan, + "summaryValuesSpan": s.summaryValuesSpan, + "addressAlias": addressAlias, "addressAliasSpan": addressAliasSpan, "formatAmount": s.formatAmount, "formatAmountWithDecimals": formatAmountWithDecimals, @@ -680,9 +682,12 @@ func formatAmountWithDecimals(a *api.Amount, d int) string { } func appendAmountSpan(rv *strings.Builder, class, amount, shortcut, txDate string) { - rv.WriteString(`") } - rv.WriteString(" ") - rv.WriteString(shortcut) + if shortcut != "" { + rv.WriteString(" ") + rv.WriteString(shortcut) + } rv.WriteString("") } -func appendWrappingAmountSpan(rv *strings.Builder, primary, symbol, classes string) { +func appendAmountWrapperSpan(rv *strings.Builder, primary, symbol, classes string) { rv.WriteString(` 0 { + appendAmountSpan(&rv, "", strconv.FormatFloat(secondaryValue, 'f', 2, 64), td.SecondaryCoin, "") + if baseValue > 0 && s.chainParser.GetChainType() == bchain.ChainEthereumType { + rv.WriteString(`(`) + appendAmountSpan(&rv, "", strconv.FormatFloat(baseValue, 'f', 6, 64), td.CoinShortcut, "") + rv.WriteString(")") + } + } else { + if td.SecondaryCoin != "" { + rv.WriteString("-") + } + } + return template.HTML(rv.String()) +} + func formatInt(i int) template.HTML { return formatInt64(int64(i)) } @@ -905,7 +919,7 @@ func formatBigInt(i *big.Int) template.HTML { return template.HTML(rv.String()) } -func addressAliasSpan(a string, td *TemplateData) template.HTML { +func getAddressAlias(a string, td *TemplateData) *api.AddressAlias { var alias api.AddressAlias var found bool if td.Block != nil { @@ -915,8 +929,24 @@ func addressAliasSpan(a string, td *TemplateData) template.HTML { } else if td.Tx != nil { alias, found = td.Tx.AddressAliases[a] } - var rv strings.Builder if !found { + return nil + } + return &alias +} + +func addressAlias(a string, td *TemplateData) string { + alias := getAddressAlias(a, td) + if alias == nil { + return "" + } + return alias.Alias +} + +func addressAliasSpan(a string, td *TemplateData) template.HTML { + var rv strings.Builder + alias := getAddressAlias(a, td) + if alias == nil { rv.WriteString(``) rv.WriteString(a) } else { @@ -934,9 +964,8 @@ func addressAliasSpan(a string, td *TemplateData) template.HTML { // called from template to support txdetail.html functionality func setTxToTemplateData(td *TemplateData, tx *api.Tx) *TemplateData { td.Tx = tx - // reset the TxBlocktimeSecondaryCoinRate if different Blocktime - if td.TxBlocktime != tx.Blocktime { - td.TxBlocktime = 0 + // reset the TxTicker if different Blocktime + if td.TxTicker != nil && td.TxTicker.Timestamp.Unix() != tx.Blocktime { td.TxSecondaryCoinRate = 0 td.TxTicker = nil } @@ -1104,11 +1133,11 @@ func (s *PublicServer) explorerAddress(w http.ResponseWriter, r *http.Request) ( s.metrics.ExplorerViews.With(common.Labels{"action": "address"}).Inc() page, _, _, filter, filterParam, _ := s.getAddressQueryParams(r, api.AccountDetailsTxHistoryLight, txsOnPage) // do not allow details to be changed by query params - address, err := s.api.GetAddress(addressParam, page, txsOnPage, api.AccountDetailsTxHistoryLight, filter) + data := s.newTemplateData(r) + address, err := s.api.GetAddress(addressParam, page, txsOnPage, api.AccountDetailsTxHistoryLight, filter, strings.ToLower(data.SecondaryCoin)) if err != nil { return errorTpl, nil, err } - data := s.newTemplateData(r) data.AddrStr = address.AddrStr data.Address = address data.Page = address.Page @@ -1253,7 +1282,7 @@ func (s *PublicServer) explorerSearch(w http.ResponseWriter, r *http.Request) (t http.Redirect(w, r, joinURL("/tx/", tx.Txid), http.StatusFound) return noTpl, nil, nil } - address, err = s.api.GetAddress(q, 0, 1, api.AccountDetailsBasic, &api.AddressFilter{Vout: api.AddressFilterVoutOff}) + address, err = s.api.GetAddress(q, 0, 1, api.AccountDetailsBasic, &api.AddressFilter{Vout: api.AddressFilterVoutOff}, "") if err == nil { http.Redirect(w, r, joinURL("/address/", address.AddrStr), http.StatusFound) return noTpl, nil, nil @@ -1451,7 +1480,8 @@ func (s *PublicServer) apiAddress(r *http.Request, apiVersion int) (interface{}, var err error s.metrics.ExplorerViews.With(common.Labels{"action": "api-address"}).Inc() page, pageSize, details, filter, _, _ := s.getAddressQueryParams(r, api.AccountDetailsTxidHistory, txsInAPI) - address, err = s.api.GetAddress(addressParam, page, pageSize, details, filter) + secondaryCoin := strings.ToLower(r.URL.Query().Get("secondary")) + address, err = s.api.GetAddress(addressParam, page, pageSize, details, filter, secondaryCoin) if err == nil && apiVersion == apiV1 { return s.api.AddressToV1(address), nil } diff --git a/server/public_ethereumtype_test.go b/server/public_ethereumtype_test.go index 30d41661..e6dcff49 100644 --- a/server/public_ethereumtype_test.go +++ b/server/public_ethereumtype_test.go @@ -24,7 +24,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Address 0.000000000123450123 FAKE

0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b

Confirmed

Balance0.000000000123450123 FAKE
Transactions2
Non-contract Transactions0
Internal Transactions0
Nonce123
ERC20 Tokens
ContractTokensTransfers
Contract 740.001000123074 S741
Contract 130.000000001000123013 S131
ERC721 Tokens
ContractTokensTransfers
Contract 20511

Transactions

ERC721 Token Transfers
0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b
ID 1 S205
Fee: 0.00008794500041041 FAKE
Unconfirmed Transaction!0 FAKE
ERC20 Token Transfers
0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b
871.180000950184 S74
0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b
7.674999999999991915 S13
Fee: 0.000216368 FAKE
Unconfirmed Transaction!0 FAKE
`, + `Trezor Fake Coin Explorer

Address address7b.eth

0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b

0.000000000123450123 FAKE

Confirmed
Balance0.000000000123450123 FAKE
Transactions2
Non-contract Transactions0
Internal Transactions0
Nonce123
ContractQuantityValueTransfers
Contract 130.000000001000123013 S131
Contract 740.001000123074 S741
ContractTokensTransfers
Contract 20511

Transactions

ERC721 Token Transfers
`, }, }, { @@ -33,7 +33,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Address 0.000000000123450093 FAKE

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e

Confirmed

Balance0.000000000123450093 FAKE
Transactions1
Non-contract Transactions1
Internal Transactions0
Nonce93
ERC1155 Tokens
ContractTokensTransfers
Contract 1111 of ID 1776, 10 of ID 18981

Transactions

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
0 FAKE
ERC1155 Token Transfers
0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
1 S111 of ID 1776, 10 S111 of ID 1898
Fee: 0.000081891755740665 FAKE
Unconfirmed Transaction!0 FAKE
`, + `Trezor Fake Coin Explorer

Address

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e

0.000000000123450093 FAKE

Confirmed
Balance0.000000000123450093 FAKE
Transactions1
Non-contract Transactions1
Internal Transactions0
Nonce93
ContractTokensTransfers
Contract 1111 S111 of ID 1776, 10 S111 of ID 18981

Transactions

0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
 
0 FAKE
ERC1155 Token Transfers
 
0x5Dc6288b35E0807A3d6fEB89b3a2Ff4aB773168e
1 S111 of ID 1776, 10 S111 of ID 1898
`, }, }, { @@ -42,14 +42,14 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Transaction

0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101

Summary

In BlockUnconfirmed
StatusSuccess
Value0 FAKE
Gas Used / Limit52025 / 78037
Gas Price0.00000004 FAKE
Fees0.002081 FAKE
RBFON

Details

Input Data
Transfer
Method ID: 0xa9059cbb
Function: transfer(address, uint256)
#TypeData
0address0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f
1uint25610000000000000000000000
Raw Transaction
`, + `Trezor Fake Coin Explorer

Transaction

0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101
In BlockUnconfirmed
StatusSuccess
Value0 FAKE
Gas Used / Limit52025 / 78037
Gas Price0.00000004 FAKE (40 Gwei)
Fees0.002081 FAKE
RBFON
ERC20 Token Transfers
Input Data

0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000
transfer(address, uint256)
#TypeData
0address0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f
1uint25610000000000000000000000
Raw Transaction
`, }, }, { name: "explorerTokenDetail " + dbtestdata.EthAddr7b, r: newGetRequest(ts.URL + "/nft/" + dbtestdata.EthAddrContractCd + "/" + "1"), status: http.StatusOK, contentType: "text/html; charset=utf-8", - body: []string{`

NFT Token Detail

Token ID1
Contract0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9 Contract 205
Contract typeERC20
`, `Loading metadata from https://ipfs.io/ipfs/cda9fc258358ecaa88845f19af595e908bb7efe9.json`}, + body: []string{`Trezor Fake Coin Explorer

NFT Token Detail

Token ID1
Contract0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9
Contract 205
Contract typeERC20
`}, }, { name: "apiIndex", @@ -70,7 +70,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","balance":"123450075","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":1,"nonTokenTxs":1,"internalTxs":1,"txids":["0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2"],"nonce":"75","tokens":[{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":2,"symbol":"S13","decimals":18,"balance":"1000075013"},{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":2,"symbol":"S74","decimals":12,"balance":"1000075074"}]}`, + `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","balance":"123450075","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":1,"nonTokenTxs":1,"internalTxs":1,"txids":["0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2"],"nonce":"75","tokens":[{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":2,"symbol":"S13","decimals":18,"balance":"1000075013"},{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":2,"symbol":"S74","decimals":12,"balance":"1000075074"}],"totalBaseValue":1.23450075e-10}`, }, }, { @@ -79,7 +79,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "application/json; charset=utf-8", body: []string{ - `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","balance":"123450123","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"transactions":[{"txid":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","vin":[{"n":0,"addresses":["0x837E3f699d85a4b0B99894567e9233dFB1DcB081"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"87945000410410","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x2","gasPrice":"0x59682f07","gas":"0x173a9","to":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","value":"0x0","input":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","hash":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","blockNumber":"0xb33b9f","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","transactionIndex":"0x1"},"receipt":{"gasUsed":"0xe506","status":"0x1","logs":[{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"},{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"}]}},"tokenTransfers":[{"type":"ERC721","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","contract":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","name":"Contract 205","symbol":"S205","decimals":18,"value":"1"}],"ethereumSpecific":{"status":1,"nonce":2,"gasLimit":95145,"gasUsed":58630,"gasPrice":"1500000007","data":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","parsedData":{"methodId":"0x23b872dd","name":""}}},{"txid":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","vin":[{"n":0,"addresses":["0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0x479CC461fEcd078F766eCc58533D6F69580CF3AC"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"216368000000000","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x1df76","gasPrice":"0x3b9aca00","gas":"0x3d090","to":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","value":"0x0","input":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","hash":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","blockNumber":"0x41eee9","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","transactionIndex":"0x24"},"internalData":{"type":1,"contract":"0d0f936ee4c93e25944694d6c121de94d9760f11","transfers":[{"type":0,"from":"4bda106325c335df99eab7fe363cac8a0ba2a24d","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000010},{"type":2,"from":"4af4114f73d1c1c903ac9e0361b379d1291808a2","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000011}],"Error":""},"receipt":{"gasUsed":"0x34d30","status":"0x1","logs":[{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f8001"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f"],"data":"0x000000000000000000000000000000000000000000000000000308fd0e798ac0"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000000000000000000000000000000000000000000000","0x5af266c0a89a07c1917deaa024414577e6c3c31c8907d079e13eb448c082594f"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000000000000000000000000006a8313d60b1f8001000000000000000000000000000000000000000000000000000308fd0e798ac0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e083a16f4b092c5729a49f9c3ed3cc171bb3d3d0c22e20b1de6063c32f399ac"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x00000000000000000000000000000000000000000000000000031855667df7a8"},{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f606b"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000000","0xb0b69dad58df6032c3b266e19b1045b19c87acd2c06fb0c598090f44b8e263aa"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f1100000000000000000000000000000000000000000000000000031855667df7a80000000000000000000000000000000000000000000000006a8313d60b1f606b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f2b0d62c44ed08f2a5adef40c875d20310a42a9d4f488bd26323256fe01c7f48"}]}},"tokenTransfers":[{"type":"ERC20","from":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7675000000000000001"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"854307892726464"},{"type":"ERC20","from":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"871180000950184"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7674999999999991915"}],"ethereumSpecific":{"status":1,"nonce":122742,"gasLimit":250000,"gasUsed":216368,"gasPrice":"1000000000","data":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","parsedData":{"methodId":"0x4f150787","name":""}}}],"nonce":"123","tokens":[{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":1,"symbol":"S74","decimals":12,"balance":"1000123074"},{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":1,"symbol":"S13","decimals":18,"balance":"1000123013"},{"type":"ERC721","name":"Contract 205","contract":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","transfers":1,"symbol":"S205","decimals":18,"ids":["1"]}],"addressAliases":{"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b":{"Type":"ENS","Alias":"address7b.eth"},"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9":{"Type":"Contract","Alias":"Contract 205"}}}`, + `{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","balance":"123450123","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"transactions":[{"txid":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","vin":[{"n":0,"addresses":["0x837E3f699d85a4b0B99894567e9233dFB1DcB081"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"87945000410410","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x2","gasPrice":"0x59682f07","gas":"0x173a9","to":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","value":"0x0","input":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","hash":"0xca7628be5c80cda77163729ec63d218ee868a399d827a4682a478c6f48a6e22a","blockNumber":"0xb33b9f","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","transactionIndex":"0x1"},"receipt":{"gasUsed":"0xe506","status":"0x1","logs":[{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"},{"address":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb081","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000001"],"data":"0x"}]}},"tokenTransfers":[{"type":"ERC721","from":"0x837E3f699d85a4b0B99894567e9233dFB1DcB081","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","contract":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","name":"Contract 205","symbol":"S205","decimals":18,"value":"1"}],"ethereumSpecific":{"status":1,"nonce":2,"gasLimit":95145,"gasUsed":58630,"gasPrice":"1500000007","data":"0x23b872dd000000000000000000000000837e3f699d85a4b0b99894567e9233dfb1dcb0810000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000000000000000000000000000000000000000000001","parsedData":{"methodId":"0x23b872dd","name":""}}},{"txid":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","vin":[{"n":0,"addresses":["0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D"],"isAddress":true}],"vout":[{"value":"0","n":0,"addresses":["0x479CC461fEcd078F766eCc58533D6F69580CF3AC"],"isAddress":true}],"blockHeight":-1,"confirmations":0,"blockTime":0,"value":"0","fees":"216368000000000","rbf":true,"coinSpecificData":{"tx":{"nonce":"0x1df76","gasPrice":"0x3b9aca00","gas":"0x3d090","to":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","value":"0x0","input":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","hash":"0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2","blockNumber":"0x41eee9","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","transactionIndex":"0x24"},"internalData":{"type":1,"contract":"0d0f936ee4c93e25944694d6c121de94d9760f11","transfers":[{"type":0,"from":"4bda106325c335df99eab7fe363cac8a0ba2a24d","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000010},{"type":2,"from":"4af4114f73d1c1c903ac9e0361b379d1291808a2","to":"9f4981531fda132e83c44680787dfa7ee31e4f8d","value":1000011}],"Error":""},"receipt":{"gasUsed":"0x34d30","status":"0x1","logs":[{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f8001"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f"],"data":"0x000000000000000000000000000000000000000000000000000308fd0e798ac0"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f","0x0000000000000000000000000000000000000000000000000000000000000000","0x5af266c0a89a07c1917deaa024414577e6c3c31c8907d079e13eb448c082594f"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000000000000000000000000006a8313d60b1f8001000000000000000000000000000000000000000000000000000308fd0e798ac0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e083a16f4b092c5729a49f9c3ed3cc171bb3d3d0c22e20b1de6063c32f399ac"},{"address":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d"],"data":"0x00000000000000000000000000000000000000000000000000031855667df7a8"},{"address":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b"],"data":"0x0000000000000000000000000000000000000000000000006a8313d60b1f606b"},{"address":"0x479CC461fEcd078F766eCc58533D6F69580CF3AC","topics":["0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3","0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b","0x0000000000000000000000000000000000000000000000000000000000000000","0xb0b69dad58df6032c3b266e19b1045b19c87acd2c06fb0c598090f44b8e263aa"],"data":"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f1100000000000000000000000000000000000000000000000000031855667df7a80000000000000000000000000000000000000000000000006a8313d60b1f606b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f2b0d62c44ed08f2a5adef40c875d20310a42a9d4f488bd26323256fe01c7f48"}]}},"tokenTransfers":[{"type":"ERC20","from":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7675000000000000001"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"854307892726464"},{"type":"ERC20","from":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","to":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","name":"Contract 74","symbol":"S74","decimals":12,"value":"871180000950184"},{"type":"ERC20","from":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","to":"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","name":"Contract 13","symbol":"S13","decimals":18,"value":"7674999999999991915"}],"ethereumSpecific":{"status":1,"nonce":122742,"gasLimit":250000,"gasUsed":216368,"gasPrice":"1000000000","data":"0x4f15078700000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000004e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000000000000004af4114f73d1c1c903ac9e0361b379d1291808a20000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000a5ef5a7656bfb0000000000000000000000000000000000000000000000000000004ba78398d5c5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfe0b9579b4ecf7a2801880f644009a324671a79754ea57c3a103c6e70d3dbef6ba69a08000000000000000000000000000000000000000000000000004f937d86afb90000000000000000000000000000000000000000000000000ab280fd8037d500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000166cfb784b7c1f3fbe8b75484603ab8adc58aaee3a46245a6579fac7077b5570018b4e0d4eb0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000308fd0e798ac00000000000000000000000000000000000000000000000006a8313d60b1f606b0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000029de0ccec59e8948e3d905b40e5542335ebc1eb4674db517d2f6392ec7fdeb3d45f3449d313ee2589819c6c79eb1c1b047adae68565c1608e3a1d1d70823febb0000000000000000000000000000000000000000000000000000000000000000234d06fe17f1202e8b07177a30eb64d14adc08cdb3fa1b3e3e0bea0f9672c02175b77c01c51d3c7e460723b27ecbc7801fd6482559a8c9999593f9a4d149c7384","parsedData":{"methodId":"0x4f150787","name":""}}}],"nonce":"123","tokens":[{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":1,"symbol":"S13","decimals":18,"balance":"1000123013"},{"type":"ERC721","name":"Contract 205","contract":"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9","transfers":1,"symbol":"S205","decimals":18,"ids":["1"]},{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":1,"symbol":"S74","decimals":12,"balance":"1000123074"}],"totalBaseValue":1.23450123e-10,"addressAliases":{"0x7B62EB7fe80350DC7EC945C0B73242cb9877FB1b":{"Type":"ENS","Alias":"address7b.eth"},"0xcdA9FC258358EcaA88845f19Af595e908bb7EfE9":{"Type":"Contract","Alias":"Contract 205"}}}`, }, }, { diff --git a/server/public_test.go b/server/public_test.go index 4b607456..403dd8e8 100644 --- a/server/public_test.go +++ b/server/public_test.go @@ -278,7 +278,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Address

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz

0.00012345 FAKE

Confirmed
Total Received0.0002469 FAKE
Total Sent0.00012345 FAKE
Final Balance0.00012345 FAKE
No. Transactions2

Transactions

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs
 
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE×
`, + `Trezor Fake Coin Explorer

Address

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz

0.00012345 FAKE

Confirmed
Total Received0.0002469 FAKE
Total Sent0.00012345 FAKE
Final Balance0.00012345 FAKE
No. Transactions2

Transactions

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs
 
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE×
`, }, }, { @@ -361,7 +361,7 @@ func httpTestsBitcoinType(t *testing.T, ts *httptest.Server) { status: http.StatusOK, contentType: "text/html; charset=utf-8", body: []string{ - `Trezor Fake Coin Explorer

Address

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz

0.00012345 FAKE

Confirmed
Total Received0.0002469 FAKE
Total Sent0.00012345 FAKE
Final Balance0.00012345 FAKE
No. Transactions2

Transactions

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs
 
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE×
`, + `Trezor Fake Coin Explorer

Address

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz

0.00012345 FAKE

Confirmed
Total Received0.0002469 FAKE
Total Sent0.00012345 FAKE
Final Balance0.00012345 FAKE
No. Transactions2

Transactions

mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
 
OP_RETURN 2020f1686f6a200 FAKE×
No Inputs
 
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE
mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz0.00012345 FAKE×
`, }, }, { diff --git a/server/websocket.go b/server/websocket.go index 15633af2..837e2bc0 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -63,7 +63,6 @@ type fiatRatesSubscription struct { // WebsocketServer is a handle to websocket server type WebsocketServer struct { - socket *websocket.Conn upgrader *websocket.Upgrader db *db.RocksDB txCache *db.TxCache @@ -483,15 +482,16 @@ func (s *WebsocketServer) onRequest(c *websocketChannel, req *websocketReq) { } type accountInfoReq struct { - Descriptor string `json:"descriptor"` - Details string `json:"details"` - Tokens string `json:"tokens"` - PageSize int `json:"pageSize"` - Page int `json:"page"` - FromHeight int `json:"from"` - ToHeight int `json:"to"` - ContractFilter string `json:"contractFilter"` - Gap int `json:"gap"` + Descriptor string `json:"descriptor"` + Details string `json:"details"` + Tokens string `json:"tokens"` + PageSize int `json:"pageSize"` + Page int `json:"page"` + FromHeight int `json:"from"` + ToHeight int `json:"to"` + ContractFilter string `json:"contractFilter"` + SecondaryCurrency string `json:"secondaryCurrency"` + Gap int `json:"gap"` } func unmarshalGetAccountInfoRequest(params []byte) (*accountInfoReq, error) { @@ -540,7 +540,7 @@ func (s *WebsocketServer) getAccountInfo(req *accountInfoReq) (res *api.Address, } a, err := s.api.GetXpubAddress(req.Descriptor, req.Page, req.PageSize, opt, &filter, req.Gap) if err != nil { - return s.api.GetAddress(req.Descriptor, req.Page, req.PageSize, opt, &filter) + return s.api.GetAddress(req.Descriptor, req.Page, req.PageSize, opt, &filter, strings.ToLower(req.SecondaryCurrency)) } return a, nil } @@ -825,6 +825,8 @@ func (s *WebsocketServer) subscribeFiatRates(c *websocketChannel, d *fiatRatesSu currency := d.Currency if currency == "" { currency = allFiatRates + } else { + currency = strings.ToLower(currency) } as, ok := s.fiatRatesSubscriptions[currency] if !ok { diff --git a/static/css/main.css b/static/css/main.css index 0e520cbc..2616a29c 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -69,6 +69,12 @@ select { border-color: #00854d; } +.base-value { + color: #757575 !important; + padding-left: 0.5rem; + font-weight: normal; +} + .badge { vertical-align: middle; filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.15)); @@ -79,6 +85,10 @@ select { --bs-badge-border-radius: 0.6rem; } +.bg-secondary { + background-color: #757575 !important; +} + .accordion { --bs-accordion-border-radius: 10px; --bs-accordion-inner-border-radius: calc(10px - 1px); @@ -93,6 +103,10 @@ select { box-shadow: none; } +.accordion-body { + letter-spacing: -0.01em; +} + .bb-group { border: 0.6rem solid #f6f6f6; background-color: #f6f6f6; @@ -464,7 +478,7 @@ span.btn-paging:hover { } .txerror { - background-color: #c51f13b3; + background-color: #c51f13a0; color: white !important; } diff --git a/static/templates/address.html b/static/templates/address.html index 1fb0e1eb..e1cc495f 100644 --- a/static/templates/address.html +++ b/static/templates/address.html @@ -1,9 +1,12 @@ {{define "specific"}}{{$addr := .Address}}{{$data := .}}
-

{{if $addr.ContractInfo}}Contract {{$addr.ContractInfo.Name}}{{if $addr.ContractInfo.Symbol}} ({{$addr.ContractInfo.Symbol}}){{end}}{{else}}Address{{end}}

+

{{if $addr.ContractInfo}}Contract {{$addr.ContractInfo.Name}}{{if $addr.ContractInfo.Symbol}} ({{$addr.ContractInfo.Symbol}}){{end}}{{else}}Address {{addressAlias $addr.AddrStr $data}}{{end}}

{{$addr.AddrStr}}
-

{{amountSpan $addr.BalanceSat $data "copyable"}}

+

+ {{formattedAmountSpan $addr.BalanceSat 0 $data.CoinShortcut $data "copyable"}} + {{if $addr.TotalFiatValue}}{{summaryValuesSpan $addr.TotalBaseValue $addr.TotalFiatValue $data}}{{end}} +

@@ -20,6 +23,26 @@ {{if eq .ChainType 1}} + + Balance + {{amountSpan $addr.BalanceSat $data "copyable"}} + + + Transactions + {{formatInt $addr.Txs}} + + + Non-contract Transactions + {{formatInt $addr.NonTokenTxs}} + + + Internal Transactions + {{formatInt $addr.InternalTxs}} + + + Nonce + {{$addr.Nonce}} + {{if $addr.ContractInfo}} {{if $addr.ContractInfo.Type}} @@ -30,116 +53,16 @@ {{if $addr.ContractInfo.CreatedInBlock}} Created in Block - {{$addr.ContractInfo.CreatedInBlock}} + {{formatUint32 $addr.ContractInfo.CreatedInBlock}} {{end}} {{if $addr.ContractInfo.DestructedInBlock}} Destructed in Block - {{$addr.ContractInfo.DestructedInBlock}} + {{formatUint32 $addr.ContractInfo.DestructedInBlock}} {{end}} {{end}} - - Balance - {{amountSpan $addr.BalanceSat $data "copyable"}} - - - Transactions - {{$addr.Txs}} - - - Non-contract Transactions - {{$addr.NonTokenTxs}} - - - Internal Transactions - {{$addr.InternalTxs}} - - - Nonce - {{$addr.Nonce}} - - {{if tokenCount $addr.Tokens "ERC20"}} - - ERC20 Tokens - - - - - - - - - {{range $t := $addr.Tokens}} - {{if eq $t.Type "ERC20"}} - - - - - - {{end}} - {{end}} - -
ContractTokensTransfers
{{if $t.Name}}{{$t.Name}}{{else}}{{$t.Contract}}{{end}}{{formatAmountWithDecimals $t.BalanceSat $t.Decimals}} {{$t.Symbol}}{{$t.Transfers}}
- - - {{end}} - {{if tokenCount $addr.Tokens "ERC721"}} - - ERC721 Tokens - - - - - - - - - {{range $t := $addr.Tokens}} - {{if eq $t.Type "ERC721"}} - - - - - - {{end}} - {{end}} - -
ContractTokensTransfers
{{if $t.Name}}{{$t.Name}}{{else}}{{$t.Contract}}{{end}} - {{range $i, $iv := $t.Ids}}{{if $i}}, {{end}}{{formatAmountWithDecimals $iv 0}}{{end}} - {{$t.Transfers}}
- - - {{end}} - {{if tokenCount $addr.Tokens "ERC1155"}} - - ERC1155 Tokens - - - - - - - - - {{range $t := $addr.Tokens}} - {{if eq $t.Type "ERC1155"}} - - - - - - {{end}} - {{end}} - -
ContractTokensTransfers
{{if $t.Name}}{{$t.Name}}{{else}}{{$t.Contract}}{{end}} - {{range $i, $iv := $t.MultiTokenValues}}{{if $i}}, {{end}}{{$iv.Value}} of ID {{$iv.Id}}{{end}} - {{$t.Transfers}}
- - - {{end}} - {{else}} Total Received @@ -178,6 +101,120 @@ {{end}} +{{if eq .ChainType 1}} +{{if tokenCount $addr.Tokens "ERC20"}} +
+
+
+ +
+
+
+ + + + + + + + + {{range $t := $addr.Tokens}} + {{if eq $t.Type "ERC20"}} + + + + + + + {{end}} + {{end}} + +
ContractQuantityValueTransfers
{{if $t.Name}}{{$t.Name}}{{else}}{{$t.Contract}}{{end}}{{formattedAmountSpan $t.BalanceSat $t.Decimals $t.Symbol $data "copyable"}}{{summaryValuesSpan $t.BaseValue $t.FiatValue $data}}{{formatInt $t.Transfers}}
+
+
+
+
+{{end}} +{{if tokenCount $addr.Tokens "ERC721"}} +
+
+
+ +
+
+
+ + + + + + + + {{range $t := $addr.Tokens}} + {{if eq $t.Type "ERC721"}} + + + + + + {{end}} + {{end}} + +
ContractTokensTransfers
{{if $t.Name}}{{$t.Name}}{{else}}{{$t.Contract}}{{end}} + {{range $i, $iv := $t.Ids}}{{if $i}}, {{end}}{{formatAmountWithDecimals $iv 0}}{{end}} + {{$t.Transfers}}
+
+
+
+
+{{end}} +{{if tokenCount $addr.Tokens "ERC1155"}} +
+
+
+ +
+
+
+ + + + + + + + {{range $t := $addr.Tokens}} + {{if eq $t.Type "ERC1155"}} + + + + + + {{end}} + {{end}} + +
ContractTokensTransfers
{{if $t.Name}}{{$t.Name}}{{else}}{{$t.Contract}}{{end}} + {{range $i, $iv := $t.MultiTokenValues}}{{if $i}}, {{end}}{{formattedAmountSpan $iv.Value 0 $t.Symbol $data ""}} of ID {{$iv.Id}}{{end}} + {{formatInt $t.Transfers}}
+
+
+
+
+{{end}} +{{end}} {{if or $addr.Transactions $addr.Filter}}

Transactions

diff --git a/static/templates/tokenDetail.html b/static/templates/tokenDetail.html index b09abe10..2bcd0267 100644 --- a/static/templates/tokenDetail.html +++ b/static/templates/tokenDetail.html @@ -6,11 +6,11 @@ Token ID - {{$data.TokenId}} + {{$data.TokenId}} NTF Name - + NTF Description @@ -18,7 +18,7 @@ Contract - {{$data.ContractInfo.Contract}}
{{$data.ContractInfo.Name}} + {{$data.ContractInfo.Contract}}
{{$data.ContractInfo.Name}} Contract type diff --git a/static/templates/txdetail_ethereumtype.html b/static/templates/txdetail_ethereumtype.html index d07b6238..58646f8a 100644 --- a/static/templates/txdetail_ethereumtype.html +++ b/static/templates/txdetail_ethereumtype.html @@ -48,7 +48,7 @@
No Outputs
{{end}}
-
+
{{amountSpan $tx.ValueOutSat $data "tx-out copyable"}}
@@ -175,7 +175,7 @@
{{range $i, $iv := $tt.MultiTokenValues}} - {{if $i}}, {{end}}{{$iv.Value}} {{$tt.Symbol}} of ID {{$iv.Id}} + {{if $i}}, {{end}}{{formattedAmountSpan $iv.Value 0 $tt.Symbol $data ""}} of ID {{$iv.Id}} {{end}}
diff --git a/static/test-websocket.html b/static/test-websocket.html index 37ab4c75..99d3bc3f 100644 --- a/static/test-websocket.html +++ b/static/test-websocket.html @@ -141,6 +141,7 @@ const from = parseInt(document.getElementById("getAccountInfoFrom").value); const to = parseInt(document.getElementById("getAccountInfoTo").value); const contractFilter = document.getElementById("getAccountInfoContract").value.trim(); + const secondaryCurrency = document.getElementById("getAccountInfoSecondaryCurrency").value.trim(); const pageSize = 10; const method = 'getAccountInfo'; const tokens = "derived"; // could be "nonzero", "used", default is "derived" i.e. all @@ -152,7 +153,8 @@ pageSize, from, to, - contractFilter + contractFilter, + secondaryCurrency, // default gap=20 }; send(method, params, function (result) { @@ -471,9 +473,10 @@
- - - + + + +