Generalize tokens instead of ERC20 tokens in API
parent
07108b8c4f
commit
2c3af5129e
68
api/types.go
68
api/types.go
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"]}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue