Generalize tokens instead of ERC20 tokens in API

pull/105/head
Martin Boehm 2019-01-07 15:45:00 +01:00
parent 07108b8c4f
commit 2c3af5129e
5 changed files with 88 additions and 73 deletions

View File

@ -104,27 +104,34 @@ type Vout struct {
Type string `json:"type,omitempty"`
}
// Erc20Token contains info about ERC20 token held by an address
type Erc20Token struct {
Contract string `json:"contract"`
Transfers int `json:"transfers"`
Name string `json:"name"`
Symbol string `json:"symbol"`
Decimals int `json:"decimals"`
BalanceSat *Amount `json:"balance,omitempty"`
ContractIndex string `json:"-"`
// TokenType specifies type of token
type TokenType string
// ERC20TokenType is Ethereum ERC20 token
const ERC20TokenType TokenType = "ERC20"
// Token contains info about tokens held by an address
type Token struct {
Type TokenType `json:"type"`
Contract string `json:"contract"`
Transfers int `json:"transfers"`
Name string `json:"name"`
Symbol string `json:"symbol"`
Decimals int `json:"decimals"`
BalanceSat *Amount `json:"balance,omitempty"`
ContractIndex string `json:"-"`
}
// TokenTransfer contains info about a token transfer done in a transaction
type TokenTransfer struct {
Type string `json:"type"`
From string `json:"from"`
To string `json:"to"`
Token string `json:"token"`
Name string `json:"name"`
Symbol string `json:"symbol"`
Decimals int `json:"decimals"`
Value *Amount `json:"value"`
Type TokenType `json:"type"`
From string `json:"from"`
To string `json:"to"`
Token string `json:"token"`
Name string `json:"name"`
Symbol string `json:"symbol"`
Decimals int `json:"decimals"`
Value *Amount `json:"value"`
}
// EthereumSpecific contains ethereum specific transaction data
@ -186,19 +193,20 @@ type AddressFilter struct {
// Address holds information about address and its transactions
type Address struct {
Paging
AddrStr string `json:"addrStr"`
BalanceSat *Amount `json:"balance"`
TotalReceivedSat *Amount `json:"totalReceived,omitempty"`
TotalSentSat *Amount `json:"totalSent,omitempty"`
UnconfirmedBalanceSat *Amount `json:"unconfirmedBalance"`
UnconfirmedTxApperances int `json:"unconfirmedTxApperances"`
TxApperances int `json:"txApperances"`
Transactions []*Tx `json:"transactions,omitempty"`
Txids []string `json:"txids,omitempty"`
Nonce string `json:"nonce,omitempty"`
Erc20Contract *bchain.Erc20Contract `json:"erc20contract,omitempty"`
Erc20Tokens []Erc20Token `json:"erc20tokens,omitempty"`
Filter string `json:"-"`
AddrStr string `json:"addrStr"`
BalanceSat *Amount `json:"balance"`
TotalReceivedSat *Amount `json:"totalReceived,omitempty"`
TotalSentSat *Amount `json:"totalSent,omitempty"`
UnconfirmedBalanceSat *Amount `json:"unconfirmedBalance"`
UnconfirmedTxs int `json:"unconfirmedTxs"`
Txs int `json:"txs"`
NonTokenTxs int `json:"nontokenTxs,omitempty"`
Transactions []*Tx `json:"transactions,omitempty"`
Txids []string `json:"txids,omitempty"`
Nonce string `json:"nonce,omitempty"`
Tokens []Token `json:"tokens,omitempty"`
Erc20Contract *bchain.Erc20Contract `json:"erc20contract,omitempty"`
Filter string `json:"-"`
}
// AddressUtxo holds information about address and its transactions

View File

@ -184,10 +184,10 @@ func (w *Worker) AddressToV1(a *Address) *AddressV1 {
TotalReceived: a.TotalReceivedSat.DecimalString(d),
TotalSent: a.TotalSentSat.DecimalString(d),
Transactions: w.transactionsToV1(a.Transactions),
TxApperances: a.TxApperances,
TxApperances: a.Txs,
Txids: a.Txids,
UnconfirmedBalance: a.UnconfirmedBalanceSat.DecimalString(d),
UnconfirmedTxApperances: a.UnconfirmedTxApperances,
UnconfirmedTxApperances: a.UnconfirmedTxs,
}
}

View File

@ -242,7 +242,7 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height uint32,
erc20c = &bchain.Erc20Contract{Name: e.Contract}
}
tokens[i] = TokenTransfer{
Type: "ERC20",
Type: ERC20TokenType,
Token: e.Contract,
From: e.From,
To: e.To,
@ -460,16 +460,16 @@ func computePaging(count, page, itemsOnPage int) (Paging, int, int, int) {
}, from, to, page
}
func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescriptor, option GetAddressOption, filter *AddressFilter) (*db.AddrBalance, []Erc20Token, *bchain.Erc20Contract, uint64, error) {
func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescriptor, option GetAddressOption, filter *AddressFilter) (*db.AddrBalance, []Token, *bchain.Erc20Contract, uint64, int, error) {
var (
ba *db.AddrBalance
erc20t []Erc20Token
tokens []Token
ci *bchain.Erc20Contract
n uint64
)
ca, err := w.db.GetAddrDescContracts(addrDesc)
if err != nil {
return nil, nil, nil, 0, NewAPIError(fmt.Sprintf("Address not found, %v", err), true)
return nil, nil, nil, 0, 0, NewAPIError(fmt.Sprintf("Address not found, %v", err), true)
}
if ca != nil {
ba = &db.AddrBalance{
@ -478,23 +478,23 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
var b *big.Int
b, err = w.chain.EthereumTypeGetBalance(addrDesc)
if err != nil {
return nil, nil, nil, 0, errors.Annotatef(err, "EthereumTypeGetBalance %v", addrDesc)
return nil, nil, nil, 0, 0, errors.Annotatef(err, "EthereumTypeGetBalance %v", addrDesc)
}
if b != nil {
ba.BalanceSat = *b
}
n, err = w.chain.EthereumTypeGetNonce(addrDesc)
if err != nil {
return nil, nil, nil, 0, errors.Annotatef(err, "EthereumTypeGetNonce %v", addrDesc)
return nil, nil, nil, 0, 0, errors.Annotatef(err, "EthereumTypeGetNonce %v", addrDesc)
}
var filterDesc bchain.AddressDescriptor
if filter.Contract != "" {
filterDesc, err = w.chainParser.GetAddrDescFromAddress(filter.Contract)
if err != nil {
return nil, nil, nil, 0, NewAPIError(fmt.Sprintf("Invalid contract filter, %v", err), true)
return nil, nil, nil, 0, 0, NewAPIError(fmt.Sprintf("Invalid contract filter, %v", err), true)
}
}
erc20t = make([]Erc20Token, len(ca.Contracts))
tokens = make([]Token, len(ca.Contracts))
var j int
for i, c := range ca.Contracts {
if len(filterDesc) > 0 {
@ -506,7 +506,7 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
}
ci, err := w.chain.EthereumTypeGetErc20ContractInfo(c.Contract)
if err != nil {
return nil, nil, nil, 0, errors.Annotatef(err, "EthereumTypeGetErc20ContractInfo %v", c.Contract)
return nil, nil, nil, 0, 0, errors.Annotatef(err, "EthereumTypeGetErc20ContractInfo %v", c.Contract)
}
if ci == nil {
ci = &bchain.Erc20Contract{}
@ -526,7 +526,8 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
} else {
b = nil
}
erc20t[j] = Erc20Token{
tokens[j] = Token{
Type: ERC20TokenType,
BalanceSat: (*Amount)(b),
Contract: ci.Contract,
Name: ci.Name,
@ -537,13 +538,13 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
}
j++
}
erc20t = erc20t[:j]
tokens = tokens[:j]
ci, err = w.chain.EthereumTypeGetErc20ContractInfo(addrDesc)
if err != nil {
return nil, nil, nil, 0, err
return nil, nil, nil, 0, 0, err
}
}
return ba, erc20t, ci, n, nil
return ba, tokens, ci, n, int(ca.NonContractTxs), nil
}
// GetAddress computes address value and gets transactions for given address
@ -559,7 +560,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetA
}
var (
ba *db.AddrBalance
erc20t []Erc20Token
tokens []Token
erc20c *bchain.Erc20Contract
txm []string
txs []*Tx
@ -568,10 +569,11 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetA
uBalSat big.Int
totalReceived, totalSent *big.Int
nonce string
nonTokenTxs int
)
if w.chainType == bchain.ChainEthereumType {
var n uint64
ba, erc20t, erc20c, n, err = w.getEthereumTypeAddressBalances(addrDesc, option, filter)
ba, tokens, erc20c, n, nonTokenTxs, err = w.getEthereumTypeAddressBalances(addrDesc, option, filter)
if err != nil {
return nil, err
}
@ -684,19 +686,20 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetA
totalSent = &ba.SentSat
}
r := &Address{
Paging: pg,
AddrStr: address,
BalanceSat: (*Amount)(&ba.BalanceSat),
TotalReceivedSat: (*Amount)(totalReceived),
TotalSentSat: (*Amount)(totalSent),
TxApperances: int(ba.Txs),
UnconfirmedBalanceSat: (*Amount)(&uBalSat),
UnconfirmedTxApperances: len(txm),
Transactions: txs,
Txids: txids,
Erc20Contract: erc20c,
Erc20Tokens: erc20t,
Nonce: nonce,
Paging: pg,
AddrStr: address,
BalanceSat: (*Amount)(&ba.BalanceSat),
TotalReceivedSat: (*Amount)(totalReceived),
TotalSentSat: (*Amount)(totalSent),
Txs: int(ba.Txs),
NonTokenTxs: nonTokenTxs,
UnconfirmedBalanceSat: (*Amount)(&uBalSat),
UnconfirmedTxs: len(txm),
Transactions: txs,
Txids: txids,
Tokens: tokens,
Erc20Contract: erc20c,
Nonce: nonce,
}
glog.Info("GetAddress ", address, " finished in ", time.Since(start))
return r, nil

View File

@ -433,7 +433,7 @@ func httpTests_BitcoinType(t *testing.T, ts *httptest.Server) {
status: http.StatusOK,
contentType: "application/json; charset=utf-8",
body: []string{
`{"page":1,"totalPages":1,"itemsOnPage":1000,"addrStr":"mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw","balance":"0","totalReceived":"1234567890123","totalSent":"1234567890123","unconfirmedBalance":"0","unconfirmedTxApperances":0,"txApperances":2,"txids":["7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75"]}`,
`{"page":1,"totalPages":1,"itemsOnPage":1000,"addrStr":"mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw","balance":"0","totalReceived":"1234567890123","totalSent":"1234567890123","unconfirmedBalance":"0","unconfirmedTxs":0,"txs":2,"txids":["7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75"]}`,
},
},
{

View File

@ -17,13 +17,17 @@
</tr>
<tr>
<td>Transactions</td>
<td class="data">{{$addr.TxApperances}}</td>
<td class="data">{{$addr.Txs}}</td>
</tr>
<tr>
<td>Non-contract Transactions</td>
<td class="data">{{$addr.NonTokenTxs}}</td>
</tr>
<tr>
<td>Nonce</td>
<td class="data">{{$addr.Nonce}}</td>
</tr>
{{- if $addr.Erc20Tokens -}}
{{- if $addr.Tokens -}}
<tr>
<td>ERC20 Tokens</td>
<td style="padding: 0;">
@ -34,11 +38,11 @@
<th>Tokens</th>
<th style="width: 15%;">Transfers</th>
</tr>
{{- range $et := $addr.Erc20Tokens -}}
{{- range $t := $addr.Tokens -}}
<tr>
<td class="data ellipsis"><a href="/address/{{$et.Contract}}">{{$et.Name}}</a></td>
<td class="data">{{formatAmountWithDecimals $et.BalanceSat $et.Decimals}} {{$et.Symbol}}</td>
<td class="data">{{$et.Transfers}}</td>
<td class="data ellipsis">{{if $t.Contract}}<a href="/address/{{$t.Contract}}">{{$t.Name}}</a>{{else}}{{$t.Name}}{{end}}</td>
<td class="data">{{formatAmountWithDecimals $t.BalanceSat $t.Decimals}} {{$t.Symbol}}</td>
<td class="data">{{$t.Transfers}}</td>
</tr>
{{- end -}}
</tbody>
@ -62,7 +66,7 @@
</tr>
<tr>
<td>No. Transactions</td>
<td class="data">{{$addr.TxApperances}}</td>
<td class="data">{{$addr.Txs}}</td>
</tr>
{{- end -}}
</tbody>
@ -76,7 +80,7 @@
</script>
</div>
</div>
{{- if $addr.UnconfirmedTxApperances -}}
{{- if $addr.UnconfirmedTxs -}}
<h3>Unconfirmed</h3>
<div class="data-div">
<table class="table data-table">
@ -87,7 +91,7 @@
</tr>
<tr>
<td>No. Transactions</td>
<td class="data">{{$addr.UnconfirmedTxApperances}}</td>
<td class="data">{{$addr.UnconfirmedTxs}}</td>
</tr>
</tbody>
</table>
@ -99,10 +103,10 @@
<option>All</option>
<option {{if eq $addr.Filter "inputs" -}} selected{{end}} value="inputs">Inputs</option>
<option {{if eq $addr.Filter "outputs" -}} selected{{end}} value="outputs">Outputs</option>
{{- if $addr.Erc20Tokens -}}
{{- if $addr.Tokens -}}
<option {{if eq $addr.Filter "0" -}} selected{{end}} value="0">Non-contract</option>
{{- range $et := $addr.Erc20Tokens -}}
<option {{if eq $addr.Filter $et.ContractIndex -}} selected{{end}} value="{{$et.ContractIndex}}">{{$et.Name}}</option>
{{- range $t := $addr.Tokens -}}
<option {{if eq $addr.Filter $t.ContractIndex -}} selected{{end}} value="{{$t.ContractIndex}}">{{$t.Name}}</option>
{{- end -}}
{{- end -}}
</select>