Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 41 additions & 60 deletions cmd/dcrdata/internal/explorer/explorerroutes.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
// Copyright (c) 2018-2024, The Decred developers
// Copyright (c) 2018-2025, The Decred developers
// Copyright (c) 2017, The dcrdata developers
// See LICENSE for details.

package explorer

import (
"context"
"encoding/hex"
"encoding/json"
"errors"
Expand Down Expand Up @@ -1389,62 +1388,38 @@ type TreasuryInfo struct {
Path string
Limit, Offset int64 // ?n=Limit&start=Offset
TxnType string // ?txntype=TxnType

// TODO: tadd and tspend can be unconfirmed. tspend for a very long time.
// NumUnconfirmed is the number of unconfirmed txns
// NumUnconfirmed int64
// UnconfirmedTxns []*dbtypes.TreasuryTx

// Transactions on the current page
Transactions []*dbtypes.TreasuryTx
NumTransactions int64 // len(Transactions) but int64 for dumb template

Balance *dbtypes.TreasuryBalance
ConvertedBalance *exchanges.Conversion
TypeCount int64

// tadd and tspend can be unconfirmed. tspend for a very long time.
Mempool *TreasuryMempoolInfo
}

// TreasuryMempoolInfo holds the treasury-related mempool transactions that are
// not yet confirmed in a block. It is used to display treasury mempool
// information on the treasury page.
type TreasuryMempoolInfo struct {
NumTSpends int
NumTAdds int
TSpends []types.MempoolTx
TAdds []types.MempoolTx
}

// TreasuryPage is the page handler for the "/treasury" path
func (exp *explorerUI) TreasuryPage(w http.ResponseWriter, r *http.Request) {
ctx := context.WithValue(r.Context(), ctxAddress, exp.pageData.HomeInfo.DevAddress)
r = r.WithContext(ctx)
if queryVals := r.URL.Query(); queryVals.Get("txntype") == "" {
queryVals.Set("txntype", "tspend")
r.URL.RawQuery = queryVals.Encode()
}

limitN := defaultAddressRows
if nParam := r.URL.Query().Get("n"); nParam != "" {
val, err := strconv.ParseUint(nParam, 10, 64)
if err != nil {
exp.StatusPage(w, defaultErrorCode, "invalid n value", "", ExpStatusError)
return
}
if int64(val) > MaxTreasuryRows {
log.Warnf("TreasuryPage: requested up to %d address rows, "+
"limiting to %d", limitN, MaxTreasuryRows)
limitN = MaxTreasuryRows
} else {
limitN = int64(val)
}
}

// Number of txns to skip (OFFSET in database query). For UX reasons, the
// "start" URL query parameter is used.
var offset int64
if startParam := r.URL.Query().Get("start"); startParam != "" {
val, err := strconv.ParseUint(startParam, 10, 64)
if err != nil {
exp.StatusPage(w, defaultErrorCode, "invalid start value", "", ExpStatusError)
return
}
offset = int64(val)
// Grab the URL query parameters
txType, txTypeStr, limitN, offset, err := parseTreasuryParams(r)
if err != nil {
log.Errorf("TreasuryPage request error: %v", err)
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}

// Transaction types to show.
txTypeStr := r.URL.Query().Get("txntype")
txType := parseTreasuryTransactionType(txTypeStr)

txns, err := exp.dataSource.TreasuryTxns(limitN, offset, txType)
if exp.timeoutErrorPage(w, err, "TreasuryTxns") {
return
Expand All @@ -1458,6 +1433,7 @@ func (exp *explorerUI) TreasuryPage(w http.ResponseWriter, r *http.Request) {
exp.pageData.RUnlock()

typeCount := treasuryTypeCount(treasuryBalance, txType)
inv := exp.MempoolInventory()

treasuryData := &TreasuryInfo{
Net: exp.ChainParams.Net.String(),
Expand All @@ -1470,6 +1446,12 @@ func (exp *explorerUI) TreasuryPage(w http.ResponseWriter, r *http.Request) {
Transactions: txns,
Balance: treasuryBalance,
TypeCount: typeCount,
Mempool: &TreasuryMempoolInfo{
NumTSpends: inv.NumTSpends,
NumTAdds: inv.NumTAdds,
TSpends: inv.TSpends,
TAdds: inv.TAdds,
},
}

xcBot := exp.xcBot
Expand All @@ -1478,19 +1460,17 @@ func (exp *explorerUI) TreasuryPage(w http.ResponseWriter, r *http.Request) {
}

// Execute the HTML template.
linkTemplate := fmt.Sprintf("/treasury?start=%%d&n=%d&txntype=%v", limitN, txType)
linkTemplate := fmt.Sprintf("/treasury?start=%%d&n=%d&txntype=%s", limitN, txTypeStr)
pageData := struct {
*CommonPageData
Data *TreasuryInfo
FiatBalance *exchanges.Conversion
Pages []pageNumber
Mempool *types.MempoolInfo
}{
CommonPageData: exp.commonData(r),
Data: treasuryData,
FiatBalance: exp.xcBot.Conversion(dcrutil.Amount(treasuryBalance.Balance).ToCoin()),
Pages: calcPages(int(typeCount), int(limitN), int(offset), linkTemplate),
Mempool: exp.MempoolInventory(),
}
str, err := exp.templates.exec("treasury", pageData)
if err != nil {
Expand Down Expand Up @@ -1679,7 +1659,7 @@ func (exp *explorerUI) AddressTable(w http.ResponseWriter, r *http.Request) {
// TreasuryTable is the handler for the "/treasurytable" path.
func (exp *explorerUI) TreasuryTable(w http.ResponseWriter, r *http.Request) {
// Grab the URL query parameters
txType, limitN, offset, err := parseTreasuryParams(r)
txType, txTypeStr, limitN, offset, err := parseTreasuryParams(r)
if err != nil {
log.Errorf("TreasuryTable request error: %v", err)
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
Expand All @@ -1698,8 +1678,7 @@ func (exp *explorerUI) TreasuryTable(w http.ResponseWriter, r *http.Request) {
bal := exp.pageData.HomeInfo.TreasuryBalance
exp.pageData.RUnlock()

linkTemplate := "/treasury" + "?start=%d&n=" + strconv.FormatInt(limitN, 10) + "&txntype=" + fmt.Sprintf("%v", txType)

linkTemplate := fmt.Sprintf("/treasury?start=%%d&n=%d&txntype=%s", limitN, txTypeStr)
response := struct {
TxnCount int64 `json:"tx_count"`
HTML string `json:"html"`
Expand Down Expand Up @@ -1751,7 +1730,7 @@ func parseAddressParams(r *http.Request) (address string, txnType dbtypes.AddrTx
return
}

tType, limitN, offsetAddrOuts, err := parsePaginationParams(r)
tType, limitN, offsetAddrOuts, err := parsePaginationParams(r, true)
txnType = dbtypes.AddrTxnViewTypeFromStr(tType)
if txnType == dbtypes.AddrTxnUnknown {
err = fmt.Errorf("unknown txntype query value")
Expand Down Expand Up @@ -1791,21 +1770,20 @@ func parseTreasuryTransactionType(txnTypeStr string) (txType stake.TxType) {

// parseTreasuryParams parses the tx filters for the treasury page. Used by both
// TreasuryPage and TreasuryTable.
func parseTreasuryParams(r *http.Request) (txType stake.TxType, limitN, offsetAddrOuts int64, err error) {
tType, limitN, offsetAddrOuts, err := parsePaginationParams(r)
txType = parseTreasuryTransactionType(tType)
func parseTreasuryParams(r *http.Request) (txType stake.TxType, txTypeStr string, limitN, offsetAddrOuts int64, err error) {
txTypeStr, limitN, offsetAddrOuts, err = parsePaginationParams(r, false)
txType = parseTreasuryTransactionType(txTypeStr)
return
}

// parsePaginationParams parses the pagination parameters from the query. The
// txnType string is returned as-is. The caller must decipher the string.
func parsePaginationParams(r *http.Request) (txnType string, limitN, offset int64, err error) {
func parsePaginationParams(r *http.Request, isAddressPage bool) (txnType string, limitN, offset int64, err error) {
// Number of outputs for the address to query the database for. The URL
// query parameter "n" is used to specify the limit (e.g. "?n=20").
limitN = defaultAddressRows

if nParam := r.URL.Query().Get("n"); nParam != "" {

var val uint64
val, err = strconv.ParseUint(nParam, 10, 64)
if err != nil {
Expand Down Expand Up @@ -1834,9 +1812,12 @@ func parsePaginationParams(r *http.Request) (txnType string, limitN, offset int6
}

// Transaction types to show.
txnType = r.URL.Query().Get("txntype")
if txnType == "" {
txnType = "all"
if txnType = r.URL.Query().Get("txntype"); txnType == "" {
if isAddressPage {
txnType = "all" // Default to all types for address page
} else {
txnType = "tspend" // Default to tspend for treasury page
}
}

return
Expand Down
22 changes: 12 additions & 10 deletions cmd/dcrdata/public/js/controllers/address_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -306,17 +306,18 @@ export default class extends Controller {
e.preventDefault()
const url = e.target.href
const parser = new URL(url)
const start = parser.searchParams.get('start')
const pagesize = parser.searchParams.get('n')
const start = parseInt(parser.searchParams.get('start'))
const pagesize = parseInt(parser.searchParams.get('n'))
const txntype = parser.searchParams.get('txntype')
this.fetchTable(txntype, pagesize, start)
}

toPage (direction) {
const params = ctrl.paginationParams
const count = ctrl.pageSize
const count = parseInt(ctrl.pageSize)
const offset = parseInt(params.offset)
const txType = ctrl.txnType
let requestedOffset = params.offset + count * direction
let requestedOffset = offset + count * direction
if (requestedOffset >= params.count) return
if (requestedOffset < 0) requestedOffset = 0
ctrl.fetchTable(txType, count, requestedOffset)
Expand Down Expand Up @@ -351,8 +352,9 @@ export default class extends Controller {

setPageability () {
const params = ctrl.paginationParams
const rowMax = params.count
const count = ctrl.pageSize
const rowMax = parseInt(params.count)
const count = parseInt(ctrl.pageSize)
const offset = parseInt(params.offset)
if (ctrl.paginationParams.count === 0) {
ctrl.paginationheaderTarget.classList.add('d-hide')
} else {
Expand All @@ -370,8 +372,8 @@ export default class extends Controller {
el.classList.add('disabled')
}
}
setAbility(ctrl.pageplusTarget, params.offset + count < rowMax)
setAbility(ctrl.pageminusTarget, params.offset - count >= 0)
setAbility(ctrl.pageplusTarget, offset + count < rowMax)
setAbility(ctrl.pageminusTarget, offset - count >= 0)
ctrl.pageSizeOptions.forEach((option) => {
if (option.value > 100) {
if (rowMax > 100) {
Expand All @@ -387,9 +389,9 @@ export default class extends Controller {
})
setAbility(ctrl.pagesizeTarget, rowMax > 20)
const suffix = rowMax > 1 ? 's' : ''
let rangeEnd = params.offset + count
let rangeEnd = offset + count
if (rangeEnd > rowMax) rangeEnd = rowMax
ctrl.rangeTarget.innerHTML = 'showing ' + (params.offset + 1) + ' &ndash; ' +
ctrl.rangeTarget.innerHTML = 'showing ' + (offset + 1) + ' &ndash; ' +
rangeEnd + ' of ' + rowMax.toLocaleString() + ' transaction' + suffix
}

Expand Down
2 changes: 1 addition & 1 deletion cmd/dcrdata/views/extras.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@
data-turbolinks-suppress-warning
></script>
<script
src="/dist/js/app.9b3571fb6f7fb11a.bundle.js"
src="/dist/js/app.ff73d6c9e125de59.bundle.js"
data-turbolinks-eval="false"
data-turbolinks-suppress-warning
></script>
Expand Down
9 changes: 4 additions & 5 deletions cmd/dcrdata/views/treasury.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
<html lang="en">
{{template "html-head" headData .CommonPageData "Decred Decentralized Treasury"}}
{{template "navbar" . }}
{{- $mempool := .Mempool -}}
{{- with .Data}}
{{- $bal := .Balance -}}
{{- $TxnCount := $bal.TxCount}}
Expand Down Expand Up @@ -158,8 +157,8 @@
</tr>
</thead>
<tbody>
{{if gt $mempool.NumTSpends 0 -}}
{{- range $mempool.TSpends -}}
{{if gt .Mempool.NumTSpends 0 -}}
{{- range .Mempool.TSpends -}}
<tr>
<td class="break-word clipboard">
<a class="hash lh1rem" href="/tx/{{.Hash}}" title="{{.Hash}}">{{.Hash}}</a>
Expand All @@ -180,7 +179,7 @@
</table>
</div>
</div>
{{if gt $mempool.NumTAdds 0 -}}{{- /* this will be rare, so only show the section header and table if needed */ -}}
{{if gt .Mempool.NumTAdds 0 -}}{{- /* this will be rare, so only show the section header and table if needed */ -}}
<div class="row">
<div class="col-sm-24">
<div class="me-auto h4 col-24">Unconfirmed Treasury Adds</div>
Expand All @@ -193,7 +192,7 @@
</tr>
</thead>
<tbody>
{{range $mempool.TAdds -}}
{{range .Mempool.TAdds -}}
<tr>
<td class="break-word clipboard">
<a class="hash lh1rem" href="/tx/{{.Hash}}">{{.Hash}}</a>
Expand Down
Loading