Return token balances from API
This commit is contained in:
parent
f57bd2e6c3
commit
74ef087d4b
32
api/types.go
32
api/types.go
@ -153,27 +153,29 @@ const (
|
||||
// the map must match all bchain.TokenTransferTypes to avoid index out of range panic
|
||||
var TokenTypeMap []TokenType = []TokenType{ERC20TokenType, ERC771TokenType, ERC1155TokenType}
|
||||
|
||||
// Token contains info about tokens held by an address
|
||||
type Token struct {
|
||||
Type TokenType `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Contract string `json:"contract,omitempty"`
|
||||
Transfers int `json:"transfers"`
|
||||
Symbol string `json:"symbol,omitempty"`
|
||||
Decimals int `json:"decimals,omitempty"`
|
||||
BalanceSat *Amount `json:"balance,omitempty"`
|
||||
TotalReceivedSat *Amount `json:"totalReceived,omitempty"`
|
||||
TotalSentSat *Amount `json:"totalSent,omitempty"`
|
||||
ContractIndex string `json:"-"`
|
||||
}
|
||||
|
||||
// TokenTransferValues contains values for ERC1155 contract
|
||||
type TokenTransferValues struct {
|
||||
Id *Amount `json:"id,omitempty"`
|
||||
Value *Amount `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
// Token contains info about tokens held by an address
|
||||
type Token struct {
|
||||
Type TokenType `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Contract string `json:"contract,omitempty"`
|
||||
Transfers int `json:"transfers"`
|
||||
Symbol string `json:"symbol,omitempty"`
|
||||
Decimals int `json:"decimals,omitempty"`
|
||||
BalanceSat *Amount `json:"balance,omitempty"`
|
||||
Ids []Amount `json:"ids,omitempty"` // multiple ERC721 tokens
|
||||
IdValues []TokenTransferValues `json:"idValues,omitempty"` // multiple ERC1155 tokens
|
||||
TotalReceivedSat *Amount `json:"totalReceived,omitempty"`
|
||||
TotalSentSat *Amount `json:"totalSent,omitempty"`
|
||||
ContractIndex string `json:"-"`
|
||||
}
|
||||
|
||||
// TokenTransfer contains info about a token transfer done in a transaction
|
||||
type TokenTransfer struct {
|
||||
Type TokenType `json:"type"`
|
||||
|
||||
@ -655,7 +655,68 @@ 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) {
|
||||
func (w *Worker) getEthereumContractBalance(addrDesc bchain.AddressDescriptor, index int, c *db.AddrContract, details AccountDetails) (*Token, error) {
|
||||
// TODO use db.contracts
|
||||
validContract := true
|
||||
|
||||
ci, err := w.chain.EthereumTypeGetErc20ContractInfo(c.Contract)
|
||||
if err != nil {
|
||||
return nil, 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
|
||||
}
|
||||
|
||||
t := Token{
|
||||
Type: ERC20TokenType,
|
||||
Contract: ci.Contract,
|
||||
Name: ci.Name,
|
||||
Symbol: ci.Symbol,
|
||||
Transfers: int(c.Txs),
|
||||
Decimals: ci.Decimals,
|
||||
ContractIndex: strconv.Itoa(index),
|
||||
}
|
||||
// return contract balances/values only at or above AccountDetailsTokenBalances
|
||||
if details >= AccountDetailsTokenBalances && validContract {
|
||||
if c.Type == bchain.ERC20 {
|
||||
// get Erc20 Contract Balance from blockchain, balance obtained from adding and subtracting transfers is not correct
|
||||
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 {
|
||||
t.BalanceSat = (*Amount)(b)
|
||||
}
|
||||
} else {
|
||||
if len(t.Ids) > 0 {
|
||||
ids := make([]Amount, len(t.Ids))
|
||||
for j := range ids {
|
||||
ids[j] = (Amount)(c.Ids[j])
|
||||
}
|
||||
t.Ids = ids
|
||||
}
|
||||
if len(t.IdValues) > 0 {
|
||||
idValues := make([]TokenTransferValues, len(t.IdValues))
|
||||
for j := range idValues {
|
||||
idValues[j].Id = (*Amount)(&c.IdValues[j].Id)
|
||||
idValues[j].Value = (*Amount)(&c.IdValues[j].Value)
|
||||
}
|
||||
t.IdValues = idValues
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &t, nil
|
||||
}
|
||||
|
||||
// a fallback method in case internal transactions are not processed and there is no indexed info about contract balance for an address
|
||||
func (w *Worker) getEthereumContractBalanceFromBlockchain(addrDesc, contract bchain.AddressDescriptor, details AccountDetails) (*Token, error) {
|
||||
var b *big.Int
|
||||
validContract := true
|
||||
ci, err := w.chain.EthereumTypeGetErc20ContractInfo(contract)
|
||||
@ -687,9 +748,9 @@ func (w *Worker) getEthereumToken(index int, addrDesc, contract bchain.AddressDe
|
||||
Contract: ci.Contract,
|
||||
Name: ci.Name,
|
||||
Symbol: ci.Symbol,
|
||||
Transfers: txs,
|
||||
Transfers: 0,
|
||||
Decimals: ci.Decimals,
|
||||
ContractIndex: strconv.Itoa(index),
|
||||
ContractIndex: "0",
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -733,7 +794,8 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
|
||||
if details > AccountDetailsBasic {
|
||||
tokens = make([]Token, len(ca.Contracts))
|
||||
var j int
|
||||
for i, c := range ca.Contracts {
|
||||
for i := range ca.Contracts {
|
||||
c := &ca.Contracts[i]
|
||||
if len(filterDesc) > 0 {
|
||||
if !bytes.Equal(filterDesc, c.Contract) {
|
||||
continue
|
||||
@ -741,26 +803,14 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
|
||||
// filter only transactions of this contract
|
||||
filter.Vout = i + db.ContractIndexOffset
|
||||
}
|
||||
t, err := w.getEthereumToken(i+db.ContractIndexOffset, addrDesc, c.Contract, details, int(c.Txs))
|
||||
t, err := w.getEthereumContractBalance(addrDesc, i+db.ContractIndexOffset, c, details)
|
||||
if err != nil {
|
||||
return nil, nil, nil, 0, 0, 0, 0, err
|
||||
}
|
||||
tokens[j] = *t
|
||||
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, 0, err
|
||||
}
|
||||
tokens = []Token{*t}
|
||||
// switch off query for transactions, there are no transactions
|
||||
filter.Vout = AddressFilterVoutQueryNotNecessary
|
||||
} else {
|
||||
tokens = tokens[:j]
|
||||
}
|
||||
tokens = tokens[:j]
|
||||
}
|
||||
ci, err = w.chain.EthereumTypeGetErc20ContractInfo(addrDesc)
|
||||
if err != nil {
|
||||
@ -781,15 +831,15 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
|
||||
nonContractTxs = int(ca.NonContractTxs)
|
||||
internalTxs = int(ca.InternalTxs)
|
||||
} else {
|
||||
// addresses without any normal transactions can have internal transactions and therefore balance
|
||||
// addresses without any normal transactions can have internal transactions that were not processed and therefore balance
|
||||
if b != nil {
|
||||
ba = &db.AddrBalance{
|
||||
BalanceSat: *b,
|
||||
}
|
||||
}
|
||||
// special handling if filtering for a contract, check the ballance of it
|
||||
// special handling if filtering for a contract, check the ballance of it in the blockchain
|
||||
if len(filterDesc) > 0 && details >= AccountDetailsTokens {
|
||||
t, err := w.getEthereumToken(0, addrDesc, filterDesc, details, 0)
|
||||
t, err := w.getEthereumContractBalanceFromBlockchain(addrDesc, filterDesc, details)
|
||||
if err != nil {
|
||||
return nil, nil, nil, 0, 0, 0, 0, err
|
||||
}
|
||||
|
||||
@ -34,7 +34,7 @@ func httpTestsEthereumType(t *testing.T, ts *httptest.Server) {
|
||||
status: http.StatusOK,
|
||||
contentType: "application/json; charset=utf-8",
|
||||
body: []string{
|
||||
`{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","balance":"123450075","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":1,"nonTokenTxs":1,"internalTxs":1,"txids":["0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2"],"nonce":"75","tokens":[{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":2,"symbol":"S13","decimals":18},{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":2,"symbol":"S74","decimals":18}],"erc20Contract":{"contract":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","name":"Contract 75","symbol":"S75","decimals":18}}`,
|
||||
`{"page":1,"totalPages":1,"itemsOnPage":1000,"address":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","balance":"123450075","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":1,"nonTokenTxs":1,"internalTxs":1,"txids":["0xc92919ad24ffd58f760b18df7949f06e1190cf54a50a0e3745a385608ed3cbf2"],"nonce":"75","tokens":[{"type":"ERC20","name":"Contract 13","contract":"0x0d0F936Ee4c93e25944694D6C121de94D9760F11","transfers":2,"symbol":"S13","decimals":18,"balance":"1000075013"},{"type":"ERC20","name":"Contract 74","contract":"0x4af4114F73d1c1C903aC9E0361b379D1291808A2","transfers":2,"symbol":"S74","decimals":18,"balance":"1000075074"}],"erc20Contract":{"contract":"0x4Bda106325C335dF99eab7fE363cAC8A0ba2a24D","name":"Contract 75","symbol":"S75","decimals":18}}`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -126,3 +126,8 @@ func (c *fakeBlockChainEthereumType) EthereumTypeGetErc20ContractInfo(contractDe
|
||||
Decimals: 18,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// EthereumTypeGetErc20ContractBalance is not supported
|
||||
func (c *fakeBlockChainEthereumType) EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc bchain.AddressDescriptor) (*big.Int, error) {
|
||||
return big.NewInt(1000000000 + int64(addrDesc[0])*1000 + int64(contractDesc[0])), nil
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user