diff --git a/api/types.go b/api/types.go index add7f7fd..75d26598 100644 --- a/api/types.go +++ b/api/types.go @@ -52,6 +52,11 @@ func NewAPIError(s string, public bool) error { // Amount is datatype holding amounts type Amount big.Int +// IsZeroBigInt if big int has zero value +func IsZeroBigInt(b *big.Int) bool { + return len(b.Bits()) == 0 +} + // MarshalJSON Amount serialization func (a *Amount) MarshalJSON() (out []byte, err error) { if a == nil { @@ -207,6 +212,7 @@ type AddressFilter struct { Contract string FromHeight uint32 ToHeight uint32 + AllTokens bool } // Address holds information about address and its transactions @@ -223,6 +229,7 @@ type Address struct { Transactions []*Tx `json:"transactions,omitempty"` Txids []string `json:"txids,omitempty"` Nonce string `json:"nonce,omitempty"` + TotalTokens int `json:"totalTokens,omitempty"` Tokens []Token `json:"tokens,omitempty"` Erc20Contract *bchain.Erc20Contract `json:"erc20contract,omitempty"` Filter string `json:"-"` diff --git a/api/worker.go b/api/worker.go index 9318ef5a..2acd69ae 100644 --- a/api/worker.go +++ b/api/worker.go @@ -825,7 +825,7 @@ func (w *Worker) GetAddressUtxo(address string, onlyConfirmed bool) ([]AddressUt } var checksum big.Int // ba can be nil if the address is only in mempool! - if ba != nil && ba.BalanceSat.Uint64() > 0 { + if ba != nil && !IsZeroBigInt(&ba.BalanceSat) { outpoints := make([]bchain.Outpoint, 0, 8) err = w.db.GetAddrDescTransactions(addrDesc, 0, ^uint32(0), func(txid string, height uint32, indexes []int32) error { for _, index := range indexes { diff --git a/api/xpub.go b/api/xpub.go index 5c35dc1a..19081344 100644 --- a/api/xpub.go +++ b/api/xpub.go @@ -233,17 +233,24 @@ func (w *Worker) GetAddressForXpub(xpub string, page int, txsOnPage int, option cachedXpubsMux.Lock() cachedXpubs[xpub] = data cachedXpubsMux.Unlock() + totalTokens := 0 tokens := make([]Token, 0, 4) for i := range data.addresses { ad := &data.addresses[i] if ad.balance != nil { - tokens = append(tokens, w.tokenFromXpubAddress(ad, 0, i)) + totalTokens++ + if filter.AllTokens || !IsZeroBigInt(&ad.balance.BalanceSat) { + tokens = append(tokens, w.tokenFromXpubAddress(ad, 0, i)) + } } } for i := range data.changeAddresses { ad := &data.changeAddresses[i] if ad.balance != nil { - tokens = append(tokens, w.tokenFromXpubAddress(ad, 1, i)) + totalTokens++ + if filter.AllTokens || !IsZeroBigInt(&ad.balance.BalanceSat) { + tokens = append(tokens, w.tokenFromXpubAddress(ad, 1, i)) + } } } var totalReceived big.Int @@ -259,7 +266,8 @@ func (w *Worker) GetAddressForXpub(xpub string, page int, txsOnPage int, option // UnconfirmedTxs: len(txm), // Transactions: txs, // Txids: txids, - Tokens: tokens, + TotalTokens: totalTokens, + Tokens: tokens, // Erc20Contract: erc20c, // Nonce: nonce, } diff --git a/server/public.go b/server/public.go index a993cf63..d40567c3 100644 --- a/server/public.go +++ b/server/public.go @@ -405,6 +405,7 @@ type TemplateData struct { TOSLink string SendTxHex string Status string + AllTokens bool } func (s *PublicServer) parseTemplates() []*template.Template { @@ -582,36 +583,40 @@ func (s *PublicServer) explorerAddress(w http.ResponseWriter, r *http.Request) ( return addressTpl, data, nil } +func (s *PublicServer) getAddressForXpub(r *http.Request, xpub string, pageSize int, option api.GetAddressOption) (*api.Address, error) { + var fn = api.AddressFilterVoutOff + page, ec := strconv.Atoi(r.URL.Query().Get("page")) + if ec != nil { + page = 0 + } + filter := r.URL.Query().Get("filter") + if len(filter) > 0 { + if filter == "inputs" { + fn = api.AddressFilterVoutInputs + } else if filter == "outputs" { + fn = api.AddressFilterVoutOutputs + } else { + fn, ec = strconv.Atoi(filter) + if ec != nil || fn < 0 { + filter = "" + fn = api.AddressFilterVoutOff + } + } + } + gap, ec := strconv.Atoi(r.URL.Query().Get("gap")) + if ec != nil { + gap = 0 + } + allAddresses, _ := strconv.ParseBool(r.URL.Query().Get("alladdresses")) + return s.api.GetAddressForXpub(xpub, page, pageSize, option, &api.AddressFilter{Vout: fn, AllTokens: allAddresses}, gap) +} + func (s *PublicServer) explorerXpub(w http.ResponseWriter, r *http.Request) (tpl, *TemplateData, error) { var address *api.Address - var filter string - var fn = api.AddressFilterVoutOff var err error s.metrics.ExplorerViews.With(common.Labels{"action": "xpub"}).Inc() if i := strings.LastIndexByte(r.URL.Path, '/'); i > 0 { - page, ec := strconv.Atoi(r.URL.Query().Get("page")) - if ec != nil { - page = 0 - } - filter = r.URL.Query().Get("filter") - if len(filter) > 0 { - if filter == "inputs" { - fn = api.AddressFilterVoutInputs - } else if filter == "outputs" { - fn = api.AddressFilterVoutOutputs - } else { - fn, ec = strconv.Atoi(filter) - if ec != nil || fn < 0 { - filter = "" - fn = api.AddressFilterVoutOff - } - } - } - gap, ec := strconv.Atoi(r.URL.Query().Get("gap")) - if ec != nil { - gap = 0 - } - address, err = s.api.GetAddressForXpub(r.URL.Path[i+1:], page, txsOnPage, api.TxHistoryLight, &api.AddressFilter{Vout: fn}, gap) + address, err = s.getAddressForXpub(r, r.URL.Path[i+1:], txsOnPage, api.TxHistoryLight) if err != nil { return errorTpl, nil, err } @@ -621,10 +626,13 @@ func (s *PublicServer) explorerXpub(w http.ResponseWriter, r *http.Request) (tpl data.Address = address data.Page = address.Page data.PagingRange, data.PrevPage, data.NextPage = getPagingRange(address.Page, address.TotalPages) + filter := r.URL.Query().Get("filter") if filter != "" { data.PageParams = template.URL("&filter=" + filter) data.Address.Filter = filter } + allAddresses := r.URL.Query().Get("alladdresses") + data.AllTokens, _ = strconv.ParseBool(allAddresses) return xpubTpl, data, nil } @@ -879,15 +887,7 @@ func (s *PublicServer) apiXpub(r *http.Request, apiVersion int) (interface{}, er var err error s.metrics.ExplorerViews.With(common.Labels{"action": "api-xpub"}).Inc() if i := strings.LastIndexByte(r.URL.Path, '/'); i > 0 { - page, ec := strconv.Atoi(r.URL.Query().Get("page")) - if ec != nil { - page = 0 - } - gap, ec := strconv.Atoi(r.URL.Query().Get("gap")) - if ec != nil { - gap = 0 - } - address, err = s.api.GetAddressForXpub(r.URL.Path[i+1:], page, txsInAPI, api.TxidHistory, &api.AddressFilter{Vout: api.AddressFilterVoutOff}, gap) + address, err = s.getAddressForXpub(r, r.URL.Path[i+1:], txsInAPI, api.TxidHistory) if err == nil && apiVersion == apiV1 { return s.api.AddressToV1(address), nil } diff --git a/static/templates/xpub.html b/static/templates/xpub.html index 94b9a283..059e32a3 100644 --- a/static/templates/xpub.html +++ b/static/templates/xpub.html @@ -25,9 +25,13 @@ No. Transactions {{$addr.Txs}} + + Total XPUB addresses + {{$addr.TotalTokens}} + {{- if $addr.Tokens -}} - XPUB addresses + {{if $data.AllTokens}}XPUB Addresses{{else}}Nonzero XPUB Addresses{{end}} @@ -45,6 +49,11 @@ {{- end -}} + {{- if not $data.AllTokens -}} + + + + {{- end -}}
{{$t.Contract}}
Show all XPUB addresses