diff --git a/api/types.go b/api/types.go index c4c2a63463..7539ff641e 100644 --- a/api/types.go +++ b/api/types.go @@ -30,6 +30,9 @@ const ( AccountDetailsTokenBalances // AccountDetailsTxidHistory - basic + token balances + txids, subject to paging AccountDetailsTxidHistory + // SYSCOIN + // AccountDetailsTxHistorySummary - basic + token balances + compact account-context tx summaries, subject to paging + AccountDetailsTxHistorySummary // AccountDetailsTxHistoryLight - basic + tokens + easily obtained tx data (not requiring requests to backend), subject to paging AccountDetailsTxHistoryLight // AccountDetailsTxHistory - basic + tokens + full tx data, subject to paging @@ -393,6 +396,11 @@ type Tx struct { CoinSpecificData json.RawMessage `json:"coinSpecificData,omitempty" ts_type:"any" ts_doc:"Blockchain-specific extended data."` ChainExtraData *TxChainExtraData `json:"chainExtraData,omitempty" ts_type:"{ payloadType: 'tron'; payload?: TronChainExtraData } | { payloadType: string; payload?: any }" ts_doc:"Additional normalized chain-specific transaction data. Use payloadType as discriminator for payload."` TokenTransfers []TokenTransfer `json:"tokenTransfers,omitempty" ts_doc:"List of token transfers that occurred in this transaction."` + // SYSCOIN: account-context transaction summary fields for compact wallet history. + Direction string `json:"direction,omitempty" ts_doc:"Transaction direction relative to the queried account, when returned by account summary endpoints."` + AddressValueInSat *Amount `json:"addressValueIn,omitempty" ts_doc:"Total input value belonging to the queried account, when returned by account summary endpoints."` + AddressValueOutSat *Amount `json:"addressValueOut,omitempty" ts_doc:"Total output value belonging to the queried account, when returned by account summary endpoints."` + AccountAssetTransfers []TokenTransfer `json:"accountAssetTransfers,omitempty" ts_doc:"Syscoin SPT transfers summarized relative to the queried account."` // SYSCOIN: SPT transaction type and optional decoded memo. TokenType *bchain.TokenType `json:"tokenType,omitempty" ts_doc:"Syscoin SPT transaction type."` Memo []byte `json:"memo,omitempty" ts_doc:"Syscoin SPT memo decoded from OP_RETURN."` diff --git a/api/worker.go b/api/worker.go index 600fe2327e..bb9b6c5bc9 100644 --- a/api/worker.go +++ b/api/worker.go @@ -1462,6 +1462,138 @@ func (t *Tx) getAddrVoutValue(addrDesc bchain.AddressDescriptor) *big.Int { return &val } +// SYSCOIN +type accountAssetTransferSummary struct { + value big.Int + symbol string + decimals int +} + +// SYSCOIN +func addrDescInSet(addrDesc bchain.AddressDescriptor, addrDescs map[string]struct{}) bool { + if len(addrDescs) == 0 { + return false + } + _, found := addrDescs[string(addrDesc)] + return found +} + +// SYSCOIN +func (w *Worker) addAccountAssetTransfer(m map[string]*accountAssetTransferSummary, assetInfo *AssetInfo, sign int) { + if assetInfo == nil || assetInfo.AssetGuid == "" || assetInfo.ValueSat == nil { + return + } + summary := m[assetInfo.AssetGuid] + if summary == nil { + summary = &accountAssetTransferSummary{ + symbol: assetInfo.Symbol, + decimals: w.chainParser.AmountDecimals(), + } + if guid, err := strconv.ParseUint(assetInfo.AssetGuid, 10, 64); err == nil { + if dbAsset, err := w.db.GetAsset(guid, nil); err == nil { + summary.symbol = string(dbAsset.AssetObj.Symbol) + summary.decimals = int(dbAsset.AssetObj.Precision) + } + } + m[assetInfo.AssetGuid] = summary + } + if summary.symbol == "" { + summary.symbol = assetInfo.Symbol + } + delta := new(big.Int).Set((*big.Int)(assetInfo.ValueSat)) + if sign < 0 { + delta.Neg(delta) + } + summary.value.Add(&summary.value, delta) +} + +// SYSCOIN +func accountAssetTransfersFromSummary(m map[string]*accountAssetTransferSummary) []TokenTransfer { + if len(m) == 0 { + return nil + } + keys := make([]string, 0, len(m)) + for guid, summary := range m { + if summary.value.Sign() != 0 { + keys = append(keys, guid) + } + } + if len(keys) == 0 { + return nil + } + sort.Strings(keys) + transfers := make([]TokenTransfer, 0, len(keys)) + for _, guid := range keys { + summary := m[guid] + value := new(big.Int).Abs(&summary.value) + transfers = append(transfers, TokenTransfer{ + Type: bchain.TokenStandardName("SPT"), + Standard: bchain.TokenStandardName("SPT"), + Contract: guid, + AssetGuid: guid, + Symbol: summary.symbol, + Decimals: summary.decimals, + Value: (*Amount)(value), + }) + } + return transfers +} + +// SYSCOIN +func (w *Worker) summarizeTxForAccount(tx *Tx, addrDescs map[string]struct{}) *Tx { + if tx == nil { + return nil + } + var valueIn, valueOut big.Int + assetTransfers := make(map[string]*accountAssetTransferSummary) + for i := range tx.Vin { + vin := &tx.Vin[i] + if !addrDescInSet(vin.AddrDesc, addrDescs) { + continue + } + if vin.ValueSat != nil { + valueIn.Add(&valueIn, (*big.Int)(vin.ValueSat)) + } + w.addAccountAssetTransfer(assetTransfers, vin.AssetInfo, -1) + } + for i := range tx.Vout { + vout := &tx.Vout[i] + if !addrDescInSet(vout.AddrDesc, addrDescs) { + continue + } + if vout.ValueSat != nil { + valueOut.Add(&valueOut, (*big.Int)(vout.ValueSat)) + } + w.addAccountAssetTransfer(assetTransfers, vout.AssetInfo, 1) + } + direction := "received" + if valueIn.Cmp(&valueOut) > 0 { + direction = "sent" + } + accountAssetTransfers := accountAssetTransfersFromSummary(assetTransfers) + return &Tx{ + Txid: tx.Txid, + Vin: []Vin{}, + Vout: []Vout{}, + Blockhash: tx.Blockhash, + Blockheight: tx.Blockheight, + Confirmations: tx.Confirmations, + ConfirmationETABlocks: tx.ConfirmationETABlocks, + ConfirmationETASeconds: tx.ConfirmationETASeconds, + Blocktime: tx.Blocktime, + ValueInSat: (*Amount)(&valueIn), + ValueOutSat: (*Amount)(&valueOut), + FeesSat: tx.FeesSat, + TokenTransfers: accountAssetTransfers, + Direction: direction, + AddressValueInSat: (*Amount)(&valueIn), + AddressValueOutSat: (*Amount)(&valueOut), + AccountAssetTransfers: accountAssetTransfers, + TokenType: tx.TokenType, + Memo: tx.Memo, + } +} + type syscoinTokenMempoolInfo struct { used bool unconfirmedTxs int @@ -1574,6 +1706,7 @@ func (w *Worker) txFromTxAddress(txid string, ta *db.TxAddresses, bi *db.BlockIn vin.ValueSat = (*Amount)(&tai.ValueSat) valInSat.Add(&valInSat, &tai.ValueSat) vin.AssetInfo = w.assetInfoToAPI(tai.AssetInfo) // SYSCOIN + vin.AddrDesc = tai.AddrDesc // SYSCOIN vin.Addresses, vin.IsAddress, err = tai.Addresses(w.chainParser) if err != nil { glog.Errorf("tai.Addresses error %v, tx %v, input %v, tai %+v", err, txid, i, tai) @@ -1592,6 +1725,7 @@ func (w *Worker) txFromTxAddress(txid string, ta *db.TxAddresses, bi *db.BlockIn vout.ValueSat = (*Amount)(&tao.ValueSat) valOutSat.Add(&valOutSat, &tao.ValueSat) vout.AssetInfo = w.assetInfoToAPI(tao.AssetInfo) // SYSCOIN + vout.AddrDesc = tao.AddrDesc // SYSCOIN vout.Addresses, vout.IsAddress, err = tao.Addresses(w.chainParser) if err != nil { glog.Errorf("tai.Addresses error %v, tx %v, output %v, tao %+v", err, txid, i, tao) @@ -1679,6 +1813,36 @@ func computePaging(count, page, itemsOnPage int) (Paging, int, int, int) { }, from, to, page } +// SYSCOIN +func computePagingWithFirstPageMempool(confirmedCount, mempoolCount, page, itemsOnPage int) (Paging, int, int, int) { + if itemsOnPage <= 0 { + itemsOnPage = 1 + } + mempoolSlots := mempoolCount + if mempoolSlots > itemsOnPage { + mempoolSlots = itemsOnPage + } + if mempoolSlots < 0 { + mempoolSlots = 0 + } + pg, from, to, page := computePaging(confirmedCount+mempoolSlots, page, itemsOnPage) + from -= mempoolSlots + if from < 0 { + from = 0 + } + to -= mempoolSlots + if to < 0 { + to = 0 + } + if from > confirmedCount { + from = confirmedCount + } + if to > confirmedCount { + to = confirmedCount + } + return pg, from, to, page +} + func (w *Worker) getEthereumContractBalance(addrDesc bchain.AddressDescriptor, index int, c *db.AddrContract, details AccountDetails, ticker *common.CurrencyRatesTicker, secondaryCoin string, erc20Balance *big.Int, erc20Batched bool) (*Token, error) { standard := bchain.EthereumTokenStandardMap[c.Standard] ci, validContract, err := w.getContractDescriptorInfo(c.Contract, standard) @@ -2047,8 +2211,8 @@ func (w *Worker) getStakingPoolsData(addrDesc bchain.AddressDescriptor) ([]Staki 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 - if option == AccountDetailsTxHistoryLight && w.chainType == bchain.ChainBitcoinType { + // SYSCOIN: summary history shares the indexed Bitcoin-type light path. + if option >= AccountDetailsTxHistorySummary && option <= AccountDetailsTxHistoryLight && w.chainType == bchain.ChainBitcoinType { ta, err := w.db.GetTxAddresses(txid) if err != nil { return nil, errors.Annotatef(err, "GetTxAddresses %v", txid) @@ -2184,6 +2348,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco if err != nil { return nil, err } + accountAddrDescs := map[string]struct{}{string(addrDesc): {}} // SYSCOIN accountChainExtraData, err = w.getAccountChainExtraData(addrDesc) if err != nil { glog.Warningf("GetAccountChainExtraData error %v, %v", err, address) @@ -2250,6 +2415,8 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco if page == 0 { if option == AccountDetailsTxidHistory { txids = append(txids, tx.Txid) + } else if option == AccountDetailsTxHistorySummary { + txs = append(txs, w.summarizeTxForAccount(tx, accountAddrDescs)) // SYSCOIN } else if option >= AccountDetailsTxHistoryLight { setIsOwnAddress(tx, address) txs = append(txs, tx) @@ -2271,12 +2438,21 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco return nil, errors.Annotatef(err, "GetBestBlock") } var from, to int - pg, from, to, page = computePaging(len(txc), page, txsOnPage) + if option == AccountDetailsTxHistorySummary { + pg, from, to, page = computePagingWithFirstPageMempool(len(txc), unconfirmedTxs, page, txsOnPage) // SYSCOIN + } else { + pg, from, to, page = computePaging(len(txc), page, txsOnPage) + } if len(txc) >= txsOnPage { if totalResults < 0 { pg.TotalPages = -1 } else { - pg, _, _, _ = computePaging(totalResults, page, txsOnPage) + // SYSCOIN + if option == AccountDetailsTxHistorySummary { + pg, _, _, _ = computePagingWithFirstPageMempool(totalResults, unconfirmedTxs, page, txsOnPage) + } else { + pg, _, _, _ = computePaging(totalResults, page, txsOnPage) + } } } for i := from; i < to; i++ { @@ -2288,8 +2464,12 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco if err != nil { return nil, err } - setIsOwnAddress(tx, address) - txs = append(txs, tx) + if option == AccountDetailsTxHistorySummary { + txs = append(txs, w.summarizeTxForAccount(tx, accountAddrDescs)) // SYSCOIN + } else { + setIsOwnAddress(tx, address) + txs = append(txs, tx) + } } } } @@ -2298,7 +2478,8 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco if page == 0 && txsOnPage > 0 { if option == AccountDetailsTxidHistory && len(txids) > txsOnPage { txids = txids[:txsOnPage] - } else if option >= AccountDetailsTxHistoryLight && len(txs) > txsOnPage { + } else if option >= AccountDetailsTxHistorySummary && len(txs) > txsOnPage { + // SYSCOIN txs = txs[:txsOnPage] } } diff --git a/api/xpub.go b/api/xpub.go index 9b8ad84544..da6df497f7 100644 --- a/api/xpub.go +++ b/api/xpub.go @@ -546,6 +546,12 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc if err != nil { return nil, err } + accountAddrDescs := make(map[string]struct{}) // SYSCOIN + for _, da := range data.addresses { + for i := range da { + accountAddrDescs[string(da[i].addrDesc)] = struct{}{} + } + } // setup filtering of txids var txidFilter func(txid *xpubTxid, ad *xpubAddress) bool if !(filter.FromHeight == 0 && filter.ToHeight == 0 && filter.Vout == AddressFilterVoutOff && filter.AssetsMask == bchain.AllMask) { @@ -592,6 +598,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc } addresses := w.newAddressesMapForAliases() xpubAssetMempool := make(map[string]map[string]*syscoinTokenMempoolInfo) // SYSCOIN + mempoolEntryCount := 0 // SYSCOIN // process mempool, only if ToHeight is not specified if filter.ToHeight == 0 && !filter.OnlyConfirmed { txmMap = make(map[string]*Tx) @@ -654,12 +661,15 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc tx.addAddrVinAssetMempool(ad.addrDesc, assetMempool) } // mempool txs are returned only on the first page, uniquely and filtered - if page == 0 && xpubMempoolTxidFilter(&txid) { + if xpubMempoolTxidFilter(&txid) { if _, added := addedMempoolEntries[txid.txid]; added { continue } addedMempoolEntries[txid.txid] = struct{}{} - mempoolEntries = append(mempoolEntries, bchain.MempoolTxidEntry{Txid: txid.txid, Time: uint32(tx.Blocktime)}) + mempoolEntryCount++ // SYSCOIN + if page == 0 { + mempoolEntries = append(mempoolEntries, bchain.MempoolTxidEntry{Txid: txid.txid, Time: uint32(tx.Blocktime)}) + } } } } @@ -667,9 +677,15 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc } // sort the entries by time descending sort.Sort(mempoolEntries) - for _, entry := range mempoolEntries { + mempoolEntriesToReturn := mempoolEntries + if option == AccountDetailsTxHistorySummary && txsOnPage > 0 && len(mempoolEntriesToReturn) > txsOnPage { + mempoolEntriesToReturn = mempoolEntriesToReturn[:txsOnPage] // SYSCOIN + } + for _, entry := range mempoolEntriesToReturn { if option == AccountDetailsTxidHistory { txids = append(txids, entry.Txid) + } else if option == AccountDetailsTxHistorySummary { + txs = append(txs, w.summarizeTxForAccount(txmMap[entry.Txid], accountAddrDescs)) // SYSCOIN } else if option >= AccountDetailsTxHistoryLight { txs = append(txs, txmMap[entry.Txid]) } @@ -710,12 +726,21 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc totalResults = -1 } var from, to int - pg, from, to, page = computePaging(len(txc), page, txsOnPage) + if option == AccountDetailsTxHistorySummary { + pg, from, to, page = computePagingWithFirstPageMempool(len(txc), mempoolEntryCount, page, txsOnPage) // SYSCOIN + } else { + pg, from, to, page = computePaging(len(txc), page, txsOnPage) + } if len(txc) >= txsOnPage { if totalResults < 0 { pg.TotalPages = -1 } else { - pg, _, _, _ = computePaging(totalResults, page, txsOnPage) + // SYSCOIN + if option == AccountDetailsTxHistorySummary { + pg, _, _, _ = computePagingWithFirstPageMempool(totalResults, mempoolEntryCount, page, txsOnPage) + } else { + pg, _, _, _ = computePaging(totalResults, page, txsOnPage) + } } } // get confirmed transactions @@ -728,6 +753,9 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc if err != nil { return nil, err } + if option == AccountDetailsTxHistorySummary { + tx = w.summarizeTxForAccount(tx, accountAddrDescs) // SYSCOIN + } txs = append(txs, tx) } } diff --git a/blockbook-api.ts b/blockbook-api.ts index 84287c7c4a..1325f10562 100644 --- a/blockbook-api.ts +++ b/blockbook-api.ts @@ -252,6 +252,18 @@ export interface Tx { chainExtraData?: TxChainExtraData; /** List of token transfers that occurred in this transaction. */ tokenTransfers?: TokenTransfer[]; + /** SYSCOIN Transaction direction relative to the queried account, when returned by account summary endpoints. */ + direction?: string; + /** Total input value belonging to the queried account, when returned by account summary endpoints. */ + addressValueIn?: string; + /** Total output value belonging to the queried account, when returned by account summary endpoints. */ + addressValueOut?: string; + /** Syscoin SPT transfers summarized relative to the queried account. */ + accountAssetTransfers?: TokenTransfer[]; + /** Syscoin SPT transaction type. */ + tokenType?: string; + /** Syscoin SPT memo decoded from OP_RETURN. */ + memo?: string; /** Ethereum-like blockchain specific data (if applicable). */ ethereumSpecific?: EthereumSpecific; /** Aliases for addresses involved in this transaction. */ @@ -684,7 +696,7 @@ export interface WsAccountInfoReq { /** Address or XPUB descriptor to query. */ descriptor: string; /** Level of detail to retrieve about the account. */ - details?: 'basic' | 'tokens' | 'tokenBalances' | 'txids' | 'txslight' | 'txs'; + details?: 'basic' | 'tokens' | 'tokenBalances' | 'txids' | 'txsummary' | 'txslight' | 'txs'; /** Which tokens to include in the account info. */ tokens?: 'derived' | 'used' | 'nonzero'; /** Optional protocol enrichments to include. Supported values currently include 'erc4626'. */ diff --git a/openapi.yaml b/openapi.yaml index 1d8e04fe87..63270c6a6d 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -448,8 +448,8 @@ paths: address. Load estimate: Variable; basic is low, token/tokenBalances and - txids/txslight are medium, and txs can be high as it grows with - pageSize, transactions, token rows, filters, and protocol enrichment. + txids/txsummary/txslight are medium, and txs can be high as it grows + with pageSize, transactions, token rows, filters, and protocol enrichment. parameters: - name: address in: path @@ -1212,12 +1212,14 @@ components: tokens adds known token rows. tokenBalances returns token rows with balances. txids adds paged transaction ids. + txsummary adds compact account-context transaction summaries for + Bitcoin-type/Syscoin account endpoints. txslight adds limited transaction details from the index. txs adds full transaction details. schema: type: string default: txids - enum: [basic, tokens, tokenBalances, txids, txslight, txs] + enum: [basic, tokens, tokenBalances, txids, txsummary, txslight, txs] Tokens: name: tokens in: query @@ -2124,6 +2126,28 @@ components: type: array items: $ref: "#/components/schemas/TokenTransfer" + direction: + type: string + description: Transaction direction relative to the queried account, when returned by account summary endpoints. + addressValueIn: + allOf: + - $ref: "#/components/schemas/AmountString" + description: Total input value belonging to the queried account, when returned by account summary endpoints. + addressValueOut: + allOf: + - $ref: "#/components/schemas/AmountString" + description: Total output value belonging to the queried account, when returned by account summary endpoints. + accountAssetTransfers: + type: array + description: Syscoin SPT transfers summarized relative to the queried account. + items: + $ref: "#/components/schemas/TokenTransfer" + tokenType: + type: string + description: Syscoin SPT transaction type. + memo: + type: string + description: Syscoin SPT memo decoded from OP_RETURN. ethereumSpecific: $ref: "#/components/schemas/EthereumSpecific" addressAliases: @@ -2751,7 +2775,7 @@ components: type: string details: type: string - enum: [basic, tokens, tokenBalances, txids, txslight, txs] + enum: [basic, tokens, tokenBalances, txids, txsummary, txslight, txs] tokens: type: string enum: [derived, used, nonzero] diff --git a/server/public.go b/server/public.go index 58a7c59ec3..bd72e6d339 100644 --- a/server/public.go +++ b/server/public.go @@ -85,6 +85,25 @@ type PublicServer struct { isFullInterface bool } +// SYSCOIN +func (s *PublicServer) supportsAccountTxSummary() bool { + return s.chainParser.GetChainType() == bchain.ChainBitcoinType +} + +// SYSCOIN +func (s *PublicServer) validateTxSummarySupported(r *http.Request, apiVersion int) error { + if r.URL.Query().Get("details") != "txsummary" { + return nil + } + if apiVersion != apiV2 { + return api.NewAPIError("details=txsummary is not supported for API v1", true) + } + if !s.supportsAccountTxSummary() { + return api.NewAPIError("details=txsummary is not supported for this chain", true) + } + return nil +} + // NewPublicServer creates new public server http interface to blockbook and returns its handle // only basic functionality is mapped, to map all functions, call func NewPublicServer(binding string, certFiles string, db *db.RocksDB, chain bchain.BlockChain, mempool bchain.Mempool, txCache *db.TxCache, explorerURL string, metrics *common.Metrics, is *common.InternalState, fiatRates *fiat.FiatRates, debugMode bool) (*PublicServer, error) { @@ -1157,6 +1176,11 @@ func (s *PublicServer) getAddressQueryParams(r *http.Request, accountDetails api accountDetails = api.AccountDetailsTokenBalances case "txids": accountDetails = api.AccountDetailsTxidHistory + // SYSCOIN + case "txsummary": + if s.supportsAccountTxSummary() { + accountDetails = api.AccountDetailsTxHistorySummary + } case "txslight": accountDetails = api.AccountDetailsTxHistoryLight case "txs": @@ -1767,6 +1791,9 @@ func (s *PublicServer) apiAddress(r *http.Request, apiVersion int) (interface{}, var address *api.Address var err error s.metrics.ExplorerViews.With(common.Labels{"action": "api-address"}).Inc() + if err := s.validateTxSummarySupported(r, apiVersion); err != nil { + return nil, err + } page, pageSize, details, filter, _, _ := s.getAddressQueryParams(r, api.AccountDetailsTxidHistory, txsInAPI) if err := s.api.ValidateProtocolsForChain(filter.Protocols); err != nil { return nil, err @@ -1812,6 +1839,10 @@ func (s *PublicServer) apiAsset(r *http.Request, apiVersion int) (interface{}, e } s.metrics.ExplorerViews.With(common.Labels{"action": "api-asset"}).Inc() page, pageSize, details, filter, _, _ := s.getAddressQueryParams(r, api.AccountDetailsTxidHistory, txsInAPI) + // SYSCOIN: txsummary is account-context only; asset history needs full tx rows. + if details == api.AccountDetailsTxHistorySummary { + return nil, api.NewAPIError("details=txsummary is not supported for asset endpoint", true) + } return s.api.GetAsset(assetParam, page, pageSize, details, filter) } @@ -1855,6 +1886,9 @@ func (s *PublicServer) apiXpub(r *http.Request, apiVersion int) (interface{}, er var address *api.Address var err error s.metrics.ExplorerViews.With(common.Labels{"action": "api-xpub"}).Inc() + if err := s.validateTxSummarySupported(r, apiVersion); err != nil { + return nil, err + } page, pageSize, details, filter, _, gap := s.getAddressQueryParams(r, api.AccountDetailsTxidHistory, txsInAPI) secondaryCoin := strings.ToLower(r.URL.Query().Get("secondary")) address, err = s.api.GetXpubAddress(xpub, page, pageSize, details, filter, gap, secondaryCoin) diff --git a/server/websocket.go b/server/websocket.go index 04d75e2319..be10268292 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -137,6 +137,11 @@ type WebsocketServer struct { requestWg sync.WaitGroup } +// SYSCOIN +func (s *WebsocketServer) supportsAccountTxSummary() bool { + return s.chainParser.GetChainType() == bchain.ChainBitcoinType +} + // NewWebsocketServer creates new websocket interface to blockbook and returns its handle func NewWebsocketServer(db *db.RocksDB, chain bchain.BlockChain, mempool bchain.Mempool, txCache *db.TxCache, metrics *common.Metrics, is *common.InternalState, fiatRates *fiat.FiatRates) (*WebsocketServer, error) { api, err := api.NewWorker(db, chain, mempool, txCache, metrics, is, fiatRates) @@ -912,6 +917,12 @@ func (s *WebsocketServer) getAccountInfo(req *WsAccountInfoReq) (res *api.Addres opt = api.AccountDetailsTokenBalances case "txids": opt = api.AccountDetailsTxidHistory + // SYSCOIN + case "txsummary": + if !s.supportsAccountTxSummary() { + return nil, api.NewAPIError("details=txsummary is not supported for this chain", true) + } + opt = api.AccountDetailsTxHistorySummary case "txslight": opt = api.AccountDetailsTxHistoryLight case "txs": diff --git a/server/ws_types.go b/server/ws_types.go index 3f746201e8..a67dc22f87 100644 --- a/server/ws_types.go +++ b/server/ws_types.go @@ -27,8 +27,9 @@ type resultError struct { // WsAccountInfoReq carries parameters for the 'getAccountInfo' method. type WsAccountInfoReq struct { - Descriptor string `json:"descriptor" ts_doc:"Address or XPUB descriptor to query."` - Details string `json:"details,omitempty" ts_type:"'basic' | 'tokens' | 'tokenBalances' | 'txids' | 'txslight' | 'txs'" ts_doc:"Level of detail to retrieve about the account."` + Descriptor string `json:"descriptor" ts_doc:"Address or XPUB descriptor to query."` + // SYSCOIN: txsummary is a compact account-context history mode for wallet lists. + Details string `json:"details,omitempty" ts_type:"'basic' | 'tokens' | 'tokenBalances' | 'txids' | 'txsummary' | 'txslight' | 'txs'" ts_doc:"Level of detail to retrieve about the account."` Tokens string `json:"tokens,omitempty" ts_type:"'derived' | 'used' | 'nonzero'" ts_doc:"Which tokens to include in the account info."` Protocols []string `json:"protocols,omitempty" ts_doc:"Optional protocol enrichments to include. Supported values currently include 'erc4626'."` PageSize int `json:"pageSize,omitempty" ts_doc:"Number of items per page, if paging is used."`