diff --git a/api/types.go b/api/types.go index 4fd1e851..c797069e 100644 --- a/api/types.go +++ b/api/types.go @@ -226,6 +226,8 @@ const ( AddressFilterVoutInputs = -2 // AddressFilterVoutOutputs specifies that only txs where the address is as output are returned AddressFilterVoutOutputs = -3 + // AddressFilterVoutQueryNotNecessary signals that query for transactions is not necessary as there are no transactions for specified contract filter + AddressFilterVoutQueryNotNecessary = -4 // TokensToReturnNonzeroBalance - return only tokens with nonzero balance TokensToReturnNonzeroBalance TokensToReturn = 0 diff --git a/api/worker.go b/api/worker.go index 4f593bfc..080c93f0 100644 --- a/api/worker.go +++ b/api/worker.go @@ -497,6 +497,44 @@ func computePaging(count, page, itemsOnPage int) (Paging, int, int, int) { }, from, to, page } +func (w *Worker) getEthereumToken(index int, addrDesc, contract bchain.AddressDescriptor, details AccountDetails, txs int) (*Token, error) { + var b *big.Int + validContract := true + ci, err := w.chain.EthereumTypeGetErc20ContractInfo(contract) + if err != nil { + return nil, errors.Annotatef(err, "EthereumTypeGetErc20ContractInfo %v", contract) + } + if ci == nil { + ci = &bchain.Erc20Contract{} + addresses, _, _ := w.chainParser.GetAddressesFromAddrDesc(contract) + if len(addresses) > 0 { + ci.Contract = addresses[0] + ci.Name = addresses[0] + } + validContract = false + } + // do not read contract balances etc in case of Basic option + if details >= AccountDetailsTokenBalances && validContract { + b, err = w.chain.EthereumTypeGetErc20ContractBalance(addrDesc, contract) + if err != nil { + // return nil, nil, nil, errors.Annotatef(err, "EthereumTypeGetErc20ContractBalance %v %v", addrDesc, c.Contract) + glog.Warningf("EthereumTypeGetErc20ContractBalance addr %v, contract %v, %v", addrDesc, contract, err) + } + } else { + b = nil + } + return &Token{ + Type: ERC20TokenType, + BalanceSat: (*Amount)(b), + Contract: ci.Contract, + Name: ci.Name, + Symbol: ci.Symbol, + Transfers: txs, + Decimals: ci.Decimals, + ContractIndex: strconv.Itoa(index), + }, nil +} + func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescriptor, details AccountDetails, filter *AddressFilter) (*db.AddrBalance, []Token, *bchain.Erc20Contract, uint64, int, int, error) { var ( ba *db.AddrBalance @@ -544,43 +582,26 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto // filter only transactions of this contract filter.Vout = i + 1 } - validContract := true - ci, err := w.chain.EthereumTypeGetErc20ContractInfo(c.Contract) + t, err := w.getEthereumToken(i+1, addrDesc, c.Contract, details, int(c.Txs)) if err != nil { - return nil, nil, nil, 0, 0, 0, errors.Annotatef(err, "EthereumTypeGetErc20ContractInfo %v", c.Contract) - } - if ci == nil { - ci = &bchain.Erc20Contract{} - addresses, _, _ := w.chainParser.GetAddressesFromAddrDesc(c.Contract) - if len(addresses) > 0 { - ci.Contract = addresses[0] - ci.Name = addresses[0] - } - validContract = false - } - // do not read contract balances etc in case of Basic option - if details >= AccountDetailsTokenBalances && validContract { - b, err = w.chain.EthereumTypeGetErc20ContractBalance(addrDesc, c.Contract) - if err != nil { - // return nil, nil, nil, errors.Annotatef(err, "EthereumTypeGetErc20ContractBalance %v %v", addrDesc, c.Contract) - glog.Warningf("EthereumTypeGetErc20ContractBalance addr %v, contract %v, %v", addrDesc, c.Contract, err) - } - } else { - b = nil - } - tokens[j] = Token{ - Type: ERC20TokenType, - BalanceSat: (*Amount)(b), - Contract: ci.Contract, - Name: ci.Name, - Symbol: ci.Symbol, - Transfers: int(c.Txs), - Decimals: ci.Decimals, - ContractIndex: strconv.Itoa(i + 1), + return nil, nil, nil, 0, 0, 0, err } + tokens[j] = *t j++ } - tokens = tokens[:j] + // special handling if filter has contract + // if the address has no transactions with given contract, check the balance, the address may have some balance even without transactions + if len(filterDesc) > 0 && j == 0 && details >= AccountDetailsTokens { + t, err := w.getEthereumToken(0, addrDesc, filterDesc, details, 0) + if err != nil { + return nil, nil, nil, 0, 0, 0, err + } + tokens = []Token{*t} + // switch off query for transactions, there are no transactions + filter.Vout = AddressFilterVoutQueryNotNecessary + } else { + tokens = tokens[:j] + } } ci, err = w.chain.EthereumTypeGetErc20ContractInfo(addrDesc) if err != nil { @@ -594,6 +615,8 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto totalResults = int(ca.NonContractTxs) } else if filter.Vout > 0 && filter.Vout-1 < len(ca.Contracts) { totalResults = int(ca.Contracts[filter.Vout-1].Txs) + } else if filter.Vout == AddressFilterVoutQueryNotNecessary { + totalResults = 0 } } nonContractTxs = int(ca.NonContractTxs) @@ -750,7 +773,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco } } // get tx history if requested by option or check mempool if there are some transactions for a new address - if option >= AccountDetailsTxidHistory { + if option >= AccountDetailsTxidHistory && filter.Vout != AddressFilterVoutQueryNotNecessary { txc, err := w.getAddressTxids(addrDesc, false, filter, (page+1)*txsOnPage) if err != nil { return nil, errors.Annotatef(err, "getAddressTxids %v false", addrDesc)