Explorer redesing tuning

This commit is contained in:
Martin Boehm 2022-12-03 00:30:20 +01:00 committed by Martin
parent 9919f1a685
commit dca00bf770
15 changed files with 221 additions and 67 deletions

View File

@ -5,7 +5,6 @@ import (
"errors"
"math/big"
"sort"
"strings"
"time"
"github.com/trezor/blockbook/bchain"
@ -190,14 +189,17 @@ func (a Tokens) Less(i, j int) bool {
} 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
if ti.Name == "" {
if tj.Name != "" {
return false
}
} else {
if tj.Name == "" {
return true
}
return ti.Name < tj.Name
}
c = strings.Compare(ti.Contract, tj.Contract)
return c == -1
return ti.Contract < tj.Contract
}
// TokenTransfer contains info about a token transfer done in a transaction
@ -329,6 +331,7 @@ type Address struct {
Nonce string `json:"nonce,omitempty"`
UsedTokens int `json:"usedTokens,omitempty"`
Tokens Tokens `json:"tokens,omitempty"`
FiatValue float64 `json:"fiatValue,omitempty"`
TokensBaseValue float64 `json:"tokensBaseValue,omitempty"`
TokensFiatValue float64 `json:"tokensFiatValue,omitempty"`
TotalBaseValue float64 `json:"totalBaseValue,omitempty"`

View File

@ -6,6 +6,7 @@ import (
"encoding/json"
"math/big"
"reflect"
"sort"
"testing"
)
@ -219,3 +220,104 @@ func TestAmount_Compare(t *testing.T) {
})
}
}
func TestTokens_Sort(t *testing.T) {
tests := []struct {
name string
unsorted Tokens
sorted Tokens
}{
{
name: "one",
unsorted: Tokens{
{
Name: "a",
Contract: "0x1",
BaseValue: 12.34,
},
},
sorted: Tokens{
{
Name: "a",
Contract: "0x1",
BaseValue: 12.34,
},
},
},
{
name: "mix",
unsorted: Tokens{
{
Name: "",
Contract: "0x6",
BaseValue: 0,
},
{
Name: "",
Contract: "0x5",
BaseValue: 0,
},
{
Name: "b",
Contract: "0x2",
BaseValue: 1,
},
{
Name: "d",
Contract: "0x4",
BaseValue: 0,
},
{
Name: "a",
Contract: "0x1",
BaseValue: 12.34,
},
{
Name: "c",
Contract: "0x3",
BaseValue: 0,
},
},
sorted: Tokens{
{
Name: "a",
Contract: "0x1",
BaseValue: 12.34,
},
{
Name: "b",
Contract: "0x2",
BaseValue: 1,
},
{
Name: "c",
Contract: "0x3",
BaseValue: 0,
},
{
Name: "d",
Contract: "0x4",
BaseValue: 0,
},
{
Name: "",
Contract: "0x5",
BaseValue: 0,
},
{
Name: "",
Contract: "0x6",
BaseValue: 0,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
sort.Sort(tt.unsorted)
if !reflect.DeepEqual(tt.unsorted, tt.sorted) {
t.Errorf("Tokens Sort got %v, want %v", tt.unsorted, tt.sorted)
}
})
}
}

View File

@ -1256,22 +1256,26 @@ 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
var secondaryRate, totalFiatValue, totalBaseValue, fiatValue float64
if secondaryCoin != "" {
ticker := w.is.GetCurrentTicker("", "")
balance, 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)
}
}
fiatValue = secondaryRate * balance
if w.chainType == bchain.ChainEthereumType {
totalBaseValue += balance + ed.tokensBaseValue
totalFiatValue = secondaryRate * totalBaseValue
}
}
r := &Address{
Paging: pg,
AddrStr: address,
@ -1286,6 +1290,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco
Transactions: txs,
Txids: txids,
Tokens: ed.tokens,
FiatValue: fiatValue,
TokensBaseValue: ed.tokensBaseValue,
TokensFiatValue: ed.tokensFiatValue,
TotalBaseValue: totalBaseValue,

View File

@ -4,6 +4,7 @@ import (
"fmt"
"math/big"
"sort"
"strconv"
"sync"
"time"
@ -387,7 +388,7 @@ func (w *Worker) getXpubData(xd *bchain.XpubDescriptor, page int, txsOnPage int,
}
// GetXpubAddress computes address value and gets transactions for given address
func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option AccountDetails, filter *AddressFilter, gap int) (*Address, error) {
func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option AccountDetails, filter *AddressFilter, gap int, secondaryCoin string) (*Address, error) {
start := time.Now()
page--
if page < 0 {
@ -567,6 +568,20 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc
setIsOwnAddresses(txs, xpubAddresses)
var totalReceived big.Int
totalReceived.Add(&data.balanceSat, &data.sentSat)
var fiatValue float64
if secondaryCoin != "" {
ticker := w.is.GetCurrentTicker("", "")
balance, err := strconv.ParseFloat((*Amount)(&data.balanceSat).DecimalString(w.chainParser.AmountDecimals()), 64)
if ticker != nil && err == nil {
r, found := ticker.Rates[secondaryCoin]
if found {
secondaryRate := float64(r)
fiatValue = secondaryRate * balance
}
}
}
addr := Address{
Paging: pg,
AddrStr: xpub,
@ -580,6 +595,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc
Txids: txids,
UsedTokens: usedTokens,
Tokens: tokens,
FiatValue: fiatValue,
XPubAddresses: xpubAddresses,
AddressAliases: w.getAddressAliases(addresses),
}

View File

@ -592,7 +592,7 @@ func (b *EthereumRPC) getCreationContractInfo(contract string, height uint32) *b
func (b *EthereumRPC) processCallTrace(call *rpcCallTrace, d *bchain.EthereumInternalData, contracts []bchain.ContractInfo, blockHeight uint32) []bchain.ContractInfo {
value, err := hexutil.DecodeBig(call.Value)
if call.Type == "CREATE" {
if call.Type == "CREATE" || call.Type == "CREATE2" {
d.Transfers = append(d.Transfers, bchain.EthereumInternalTransfer{
Type: bchain.CREATE,
Value: *value,
@ -600,7 +600,6 @@ func (b *EthereumRPC) processCallTrace(call *rpcCallTrace, d *bchain.EthereumInt
To: call.To, // new contract address
})
contracts = append(contracts, *b.getCreationContractInfo(call.To, blockHeight))
} else if call.Type == "SELFDESTRUCT" {
d.Transfers = append(d.Transfers, bchain.EthereumInternalTransfer{
Type: bchain.SELFDESTRUCT,

View File

@ -850,8 +850,12 @@ func (s *PublicServer) summaryValuesSpan(baseValue float64, secondaryValue float
rv.WriteString(")</span>")
}
} else {
if td.SecondaryCoin != "" {
rv.WriteString("-")
if baseValue > 0 {
appendAmountSpan(&rv, "", strconv.FormatFloat(baseValue, 'f', 6, 64), td.CoinShortcut, "")
} else {
if td.SecondaryCoin != "" {
rv.WriteString("-")
}
}
}
return template.HTML(rv.String())
@ -1184,16 +1188,16 @@ func (s *PublicServer) explorerXpub(w http.ResponseWriter, r *http.Request) (tpl
return errorTpl, nil, api.NewAPIError("Missing xpub", true)
}
s.metrics.ExplorerViews.With(common.Labels{"action": "xpub"}).Inc()
page, _, _, filter, filterParam, gap := s.getAddressQueryParams(r, api.AccountDetailsTxHistoryLight, txsOnPage)
// do not allow txsOnPage and details to be changed by query params
address, err := s.api.GetXpubAddress(xpub, page, txsOnPage, api.AccountDetailsTxHistoryLight, filter, gap)
page, _, _, filter, filterParam, gap := s.getAddressQueryParams(r, api.AccountDetailsTxHistoryLight, txsOnPage)
data := s.newTemplateData(r)
address, err := s.api.GetXpubAddress(xpub, page, txsOnPage, api.AccountDetailsTxHistoryLight, filter, gap, strings.ToLower(data.SecondaryCoin))
if err != nil {
if err == api.ErrUnsupportedXpub {
err = api.NewAPIError("XPUB functionality is not supported", true)
}
return errorTpl, nil, err
}
data := s.newTemplateData(r)
data.AddrStr = address.AddrStr
data.Address = address
data.Page = address.Page
@ -1267,7 +1271,7 @@ func (s *PublicServer) explorerSearch(w http.ResponseWriter, r *http.Request) (t
var err error
s.metrics.ExplorerViews.With(common.Labels{"action": "search"}).Inc()
if len(q) > 0 {
address, err = s.api.GetXpubAddress(q, 0, 1, api.AccountDetailsBasic, &api.AddressFilter{Vout: api.AddressFilterVoutOff}, 0)
address, err = s.api.GetXpubAddress(q, 0, 1, api.AccountDetailsBasic, &api.AddressFilter{Vout: api.AddressFilterVoutOff}, 0, "")
if err == nil {
http.Redirect(w, r, joinURL("/xpub/", url.QueryEscape(address.AddrStr)), http.StatusFound)
return noTpl, nil, nil
@ -1501,7 +1505,8 @@ func (s *PublicServer) apiXpub(r *http.Request, apiVersion int) (interface{}, er
var err error
s.metrics.ExplorerViews.With(common.Labels{"action": "api-xpub"}).Inc()
page, pageSize, details, filter, _, gap := s.getAddressQueryParams(r, api.AccountDetailsTxidHistory, txsInAPI)
address, err = s.api.GetXpubAddress(xpub, page, pageSize, details, filter, gap)
secondaryCoin := strings.ToLower(r.URL.Query().Get("secondary"))
address, err = s.api.GetXpubAddress(xpub, page, pageSize, details, filter, gap, secondaryCoin)
if err == nil && apiVersion == apiV1 {
return s.api.AddressToV1(address), nil
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -538,7 +538,7 @@ func (s *WebsocketServer) getAccountInfo(req *accountInfoReq) (res *api.Address,
if req.PageSize == 0 {
req.PageSize = txsOnPage
}
a, err := s.api.GetXpubAddress(req.Descriptor, req.Page, req.PageSize, opt, &filter, req.Gap)
a, err := s.api.GetXpubAddress(req.Descriptor, req.Page, req.PageSize, opt, &filter, req.Gap, strings.ToLower(req.SecondaryCurrency))
if err != nil {
return s.api.GetAddress(req.Descriptor, req.Page, req.PageSize, opt, &filter, strings.ToLower(req.SecondaryCurrency))
}

View File

@ -15,7 +15,7 @@ body {
body {
min-height: 100%;
margin: 0;
background: linear-gradient(to bottom, #f6f6f6 300px, #e5e5e5 0), #e5e5e5;
background: linear-gradient(to bottom, #f6f6f6 360px, #e5e5e5 0), #e5e5e5;
background-repeat: no-repeat;
}
@ -588,6 +588,16 @@ span.btn-paging:hover {
@media (max-width: 768px) {
body {
font-size: 0.8rem;
background: linear-gradient(to bottom, #f6f6f6 500px, #e5e5e5 0), #e5e5e5;
}
.container {
padding-left: 2px;
padding-right: 2px;
}
.accordion-body {
padding: var(--bs-accordion-body-padding-y) 0;
}
.octicon {
@ -595,6 +605,10 @@ span.btn-paging:hover {
margin-top: -2px;
}
.unconfirmed {
padding: 0.1rem 0.8rem;
}
.btn {
--bs-btn-font-size: 0.8rem;
}

View File

@ -1,12 +1,19 @@
{{define "specific"}}{{$addr := .Address}}{{$data := .}}
<div class="row">
<div class="row g-0 ms-2 ms-lg-0">
<div class="col-md-10 order-2 order-md-1">
<h1>{{if $addr.ContractInfo}}Contract {{$addr.ContractInfo.Name}}{{if $addr.ContractInfo.Symbol}} ({{$addr.ContractInfo.Symbol}}){{end}}{{else}}Address {{addressAlias $addr.AddrStr $data}}{{end}}</h1>
<h5 class="col-12 d-flex h-data pb-2"><span class="ellipsis copyable">{{$addr.AddrStr}}</span></h5>
<h4>
{{formattedAmountSpan $addr.BalanceSat 0 $data.CoinShortcut $data "copyable"}}
{{if $addr.TotalFiatValue}}<span class="ps-5"{{if eq .ChainType 1}} tt="Address value including tokens"{{end}}>{{summaryValuesSpan $addr.TotalBaseValue $addr.TotalFiatValue $data}}</span>{{end}}
<h4 class="row">
<div class="col-lg-6">{{formattedAmountSpan $addr.BalanceSat 0 $data.CoinShortcut $data "copyable"}}</div>
{{if $addr.FiatValue}}<div class="col-lg-6">{{summaryValuesSpan 0 $addr.FiatValue $data}}</div>{{end}}
</h4>
{{if gt $addr.TotalFiatValue $addr.FiatValue}}
<div class="row g-0 small text-muted">Including Tokens</div>
<h4 class="row">
<div class="col-lg-6">{{summaryValuesSpan $addr.TotalBaseValue 0 $data}}</div>
<div class="col-lg-6">{{summaryValuesSpan 0 $addr.TotalFiatValue $data}}</div>
</h4>
{{end}}
</div>
<div class="col-md-2 order-1 order-md-2 d-flex justify-content-center justify-content-md-end mb-3 mb-md-0">
<div id="qrcode"></div>
@ -121,7 +128,7 @@
<th style="width: 25%;">Contract</th>
<th style="width: 30%;">Quantity</th>
<th style="width: 35%;">Value</th>
<th class="text-end" style="width: 10%;">Transfers</th>
<th class="text-end" style="width: 10%;"><span class="d-none d-md-block">Transfers</span><span class="d-block d-md-none">#</span></th>
</tr>
{{range $t := $addr.Tokens}}
{{if eq $t.Type "ERC20"}}
@ -157,7 +164,7 @@
<tr>
<th style="width: 25%;">Contract</th>
<th style="width: 65%;">Tokens</th>
<th class="text-end" style="width: 10%;">Transfers</th>
<th class="text-end" style="width: 10%;"><span class="d-none d-md-block">Transfers</span><span class="d-block d-md-none">#</span></th>
</tr>
{{range $t := $addr.Tokens}}
{{if eq $t.Type "ERC721"}}
@ -194,7 +201,7 @@
<tr>
<th style="width: 25%;">Contract</th>
<th style="width: 65%;">Tokens</th>
<th class="text-end" style="width: 10%;">Transfers</th>
<th class="text-end" style="width: 10%;"><span class="d-none d-md-block">Transfers</span><span class="d-block d-md-none">#</span></th>
</tr>
{{range $t := $addr.Tokens}}
{{if eq $t.Type "ERC1155"}}

View File

@ -82,7 +82,7 @@
<a class="nav-link" href="/sendtx">Send Transaction</a>
</span>
<span class="navbar-nav ml-md-auto d-lg-flex d-none">
<a class="nav-link" href="http://trezor.io/compare" target="_blank" rel="noopener noreferrer">Don't have a Trezor? Get one!</a>
<a class="nav-link" href="https://trezor.io/compare" target="_blank" rel="noopener noreferrer">Don't have a Trezor? Get one!</a>
</span>
</nav>
</div>

View File

@ -48,7 +48,7 @@
</tr>
<tr>
<td>Transactions in Mempool</td>
<td>{{if .InternalExplorer}}<a href="/mempool">{{$bb.MempoolSize}}</a>{{else}}{{formatInt $bb.MempoolSize}}{{end}}</td>
<td>{{if .InternalExplorer}}<a href="/mempool">{{formatInt $bb.MempoolSize}}</a>{{else}}{{formatInt $bb.MempoolSize}}{{end}}</td>
</tr>
{{if $bb.HasFiatRates}}
<tr>

View File

@ -111,8 +111,8 @@
<div id="inputDataBody" class="accordion-collapse collapse" aria-labelledby="inputDataHeading" data-bs-parent="#inputData">
<div class="accordion-body">
<div class="row">
<div class="col-12"><span class="copyable" style="overflow-wrap: break-word;">{{$tx.EthereumSpecific.Data}}</span></div>
<div class="col-12 pt-2"><span class="copyable">{{$tx.EthereumSpecific.ParsedData.Function}}</span></div>
<div class="col-12 mx-1 mx-md-0"><span class="copyable" style="overflow-wrap: break-word;">{{$tx.EthereumSpecific.Data}}</span></div>
<div class="col-12 mx-1 mx-md-0 pt-2"><span class="copyable">{{$tx.EthereumSpecific.ParsedData.Function}}</span></div>
{{if $tx.EthereumSpecific.ParsedData.Params}}
<div class="col-12">
<table class="table data-table mt-2 mb-0">

View File

@ -3,7 +3,10 @@
<div class="col-md-10 order-2 order-md-1">
<h1>XPUB</h1>
<h5 class="col-12 d-flex h-data pb-2"><span class="ellipsis copyable">{{$addr.AddrStr}}</span></h5>
<h4>{{amountSpan $addr.BalanceSat $data "copyable"}}</h4>
<h4 class="row">
<div class="col-lg-6">{{formattedAmountSpan $addr.BalanceSat 0 $data.CoinShortcut $data "copyable"}}</div>
{{if $addr.FiatValue}}<div class="col-lg-6">{{summaryValuesSpan 0 $addr.FiatValue $data}}</div>{{end}}
</h4>
</div>
<div class="col-md-2 order-1 order-md-2 d-flex justify-content-center justify-content-md-end mb-3 mb-md-0">
<div id="qrcode"></div>
@ -60,7 +63,7 @@
<td>{{amountSpan $t.BalanceSat $data "copyable"}}</td>
<td>{{formatInt $t.Transfers}}</td>
<td>{{$t.Path}}</td>
</tr>
</tr>
{{end}}
{{else}}
<tr>