Generalize tokens instead of ERC20 tokens in API
This commit is contained in:
parent
07108b8c4f
commit
2c3af5129e
68
api/types.go
68
api/types.go
@ -104,27 +104,34 @@ type Vout struct {
|
|||||||
Type string `json:"type,omitempty"`
|
Type string `json:"type,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Erc20Token contains info about ERC20 token held by an address
|
// TokenType specifies type of token
|
||||||
type Erc20Token struct {
|
type TokenType string
|
||||||
Contract string `json:"contract"`
|
|
||||||
Transfers int `json:"transfers"`
|
// ERC20TokenType is Ethereum ERC20 token
|
||||||
Name string `json:"name"`
|
const ERC20TokenType TokenType = "ERC20"
|
||||||
Symbol string `json:"symbol"`
|
|
||||||
Decimals int `json:"decimals"`
|
// Token contains info about tokens held by an address
|
||||||
BalanceSat *Amount `json:"balance,omitempty"`
|
type Token struct {
|
||||||
ContractIndex string `json:"-"`
|
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
|
// TokenTransfer contains info about a token transfer done in a transaction
|
||||||
type TokenTransfer struct {
|
type TokenTransfer struct {
|
||||||
Type string `json:"type"`
|
Type TokenType `json:"type"`
|
||||||
From string `json:"from"`
|
From string `json:"from"`
|
||||||
To string `json:"to"`
|
To string `json:"to"`
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Symbol string `json:"symbol"`
|
Symbol string `json:"symbol"`
|
||||||
Decimals int `json:"decimals"`
|
Decimals int `json:"decimals"`
|
||||||
Value *Amount `json:"value"`
|
Value *Amount `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// EthereumSpecific contains ethereum specific transaction data
|
// EthereumSpecific contains ethereum specific transaction data
|
||||||
@ -186,19 +193,20 @@ type AddressFilter struct {
|
|||||||
// Address holds information about address and its transactions
|
// Address holds information about address and its transactions
|
||||||
type Address struct {
|
type Address struct {
|
||||||
Paging
|
Paging
|
||||||
AddrStr string `json:"addrStr"`
|
AddrStr string `json:"addrStr"`
|
||||||
BalanceSat *Amount `json:"balance"`
|
BalanceSat *Amount `json:"balance"`
|
||||||
TotalReceivedSat *Amount `json:"totalReceived,omitempty"`
|
TotalReceivedSat *Amount `json:"totalReceived,omitempty"`
|
||||||
TotalSentSat *Amount `json:"totalSent,omitempty"`
|
TotalSentSat *Amount `json:"totalSent,omitempty"`
|
||||||
UnconfirmedBalanceSat *Amount `json:"unconfirmedBalance"`
|
UnconfirmedBalanceSat *Amount `json:"unconfirmedBalance"`
|
||||||
UnconfirmedTxApperances int `json:"unconfirmedTxApperances"`
|
UnconfirmedTxs int `json:"unconfirmedTxs"`
|
||||||
TxApperances int `json:"txApperances"`
|
Txs int `json:"txs"`
|
||||||
Transactions []*Tx `json:"transactions,omitempty"`
|
NonTokenTxs int `json:"nontokenTxs,omitempty"`
|
||||||
Txids []string `json:"txids,omitempty"`
|
Transactions []*Tx `json:"transactions,omitempty"`
|
||||||
Nonce string `json:"nonce,omitempty"`
|
Txids []string `json:"txids,omitempty"`
|
||||||
Erc20Contract *bchain.Erc20Contract `json:"erc20contract,omitempty"`
|
Nonce string `json:"nonce,omitempty"`
|
||||||
Erc20Tokens []Erc20Token `json:"erc20tokens,omitempty"`
|
Tokens []Token `json:"tokens,omitempty"`
|
||||||
Filter string `json:"-"`
|
Erc20Contract *bchain.Erc20Contract `json:"erc20contract,omitempty"`
|
||||||
|
Filter string `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddressUtxo holds information about address and its transactions
|
// AddressUtxo holds information about address and its transactions
|
||||||
|
|||||||
@ -184,10 +184,10 @@ func (w *Worker) AddressToV1(a *Address) *AddressV1 {
|
|||||||
TotalReceived: a.TotalReceivedSat.DecimalString(d),
|
TotalReceived: a.TotalReceivedSat.DecimalString(d),
|
||||||
TotalSent: a.TotalSentSat.DecimalString(d),
|
TotalSent: a.TotalSentSat.DecimalString(d),
|
||||||
Transactions: w.transactionsToV1(a.Transactions),
|
Transactions: w.transactionsToV1(a.Transactions),
|
||||||
TxApperances: a.TxApperances,
|
TxApperances: a.Txs,
|
||||||
Txids: a.Txids,
|
Txids: a.Txids,
|
||||||
UnconfirmedBalance: a.UnconfirmedBalanceSat.DecimalString(d),
|
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}
|
erc20c = &bchain.Erc20Contract{Name: e.Contract}
|
||||||
}
|
}
|
||||||
tokens[i] = TokenTransfer{
|
tokens[i] = TokenTransfer{
|
||||||
Type: "ERC20",
|
Type: ERC20TokenType,
|
||||||
Token: e.Contract,
|
Token: e.Contract,
|
||||||
From: e.From,
|
From: e.From,
|
||||||
To: e.To,
|
To: e.To,
|
||||||
@ -460,16 +460,16 @@ func computePaging(count, page, itemsOnPage int) (Paging, int, int, int) {
|
|||||||
}, from, to, page
|
}, 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 (
|
var (
|
||||||
ba *db.AddrBalance
|
ba *db.AddrBalance
|
||||||
erc20t []Erc20Token
|
tokens []Token
|
||||||
ci *bchain.Erc20Contract
|
ci *bchain.Erc20Contract
|
||||||
n uint64
|
n uint64
|
||||||
)
|
)
|
||||||
ca, err := w.db.GetAddrDescContracts(addrDesc)
|
ca, err := w.db.GetAddrDescContracts(addrDesc)
|
||||||
if err != nil {
|
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 {
|
if ca != nil {
|
||||||
ba = &db.AddrBalance{
|
ba = &db.AddrBalance{
|
||||||
@ -478,23 +478,23 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
|
|||||||
var b *big.Int
|
var b *big.Int
|
||||||
b, err = w.chain.EthereumTypeGetBalance(addrDesc)
|
b, err = w.chain.EthereumTypeGetBalance(addrDesc)
|
||||||
if err != nil {
|
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 {
|
if b != nil {
|
||||||
ba.BalanceSat = *b
|
ba.BalanceSat = *b
|
||||||
}
|
}
|
||||||
n, err = w.chain.EthereumTypeGetNonce(addrDesc)
|
n, err = w.chain.EthereumTypeGetNonce(addrDesc)
|
||||||
if err != nil {
|
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
|
var filterDesc bchain.AddressDescriptor
|
||||||
if filter.Contract != "" {
|
if filter.Contract != "" {
|
||||||
filterDesc, err = w.chainParser.GetAddrDescFromAddress(filter.Contract)
|
filterDesc, err = w.chainParser.GetAddrDescFromAddress(filter.Contract)
|
||||||
if err != nil {
|
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
|
var j int
|
||||||
for i, c := range ca.Contracts {
|
for i, c := range ca.Contracts {
|
||||||
if len(filterDesc) > 0 {
|
if len(filterDesc) > 0 {
|
||||||
@ -506,7 +506,7 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
|
|||||||
}
|
}
|
||||||
ci, err := w.chain.EthereumTypeGetErc20ContractInfo(c.Contract)
|
ci, err := w.chain.EthereumTypeGetErc20ContractInfo(c.Contract)
|
||||||
if err != nil {
|
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 {
|
if ci == nil {
|
||||||
ci = &bchain.Erc20Contract{}
|
ci = &bchain.Erc20Contract{}
|
||||||
@ -526,7 +526,8 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
|
|||||||
} else {
|
} else {
|
||||||
b = nil
|
b = nil
|
||||||
}
|
}
|
||||||
erc20t[j] = Erc20Token{
|
tokens[j] = Token{
|
||||||
|
Type: ERC20TokenType,
|
||||||
BalanceSat: (*Amount)(b),
|
BalanceSat: (*Amount)(b),
|
||||||
Contract: ci.Contract,
|
Contract: ci.Contract,
|
||||||
Name: ci.Name,
|
Name: ci.Name,
|
||||||
@ -537,13 +538,13 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
|
|||||||
}
|
}
|
||||||
j++
|
j++
|
||||||
}
|
}
|
||||||
erc20t = erc20t[:j]
|
tokens = tokens[:j]
|
||||||
ci, err = w.chain.EthereumTypeGetErc20ContractInfo(addrDesc)
|
ci, err = w.chain.EthereumTypeGetErc20ContractInfo(addrDesc)
|
||||||
if err != nil {
|
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
|
// 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 (
|
var (
|
||||||
ba *db.AddrBalance
|
ba *db.AddrBalance
|
||||||
erc20t []Erc20Token
|
tokens []Token
|
||||||
erc20c *bchain.Erc20Contract
|
erc20c *bchain.Erc20Contract
|
||||||
txm []string
|
txm []string
|
||||||
txs []*Tx
|
txs []*Tx
|
||||||
@ -568,10 +569,11 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetA
|
|||||||
uBalSat big.Int
|
uBalSat big.Int
|
||||||
totalReceived, totalSent *big.Int
|
totalReceived, totalSent *big.Int
|
||||||
nonce string
|
nonce string
|
||||||
|
nonTokenTxs int
|
||||||
)
|
)
|
||||||
if w.chainType == bchain.ChainEthereumType {
|
if w.chainType == bchain.ChainEthereumType {
|
||||||
var n uint64
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -684,19 +686,20 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option GetA
|
|||||||
totalSent = &ba.SentSat
|
totalSent = &ba.SentSat
|
||||||
}
|
}
|
||||||
r := &Address{
|
r := &Address{
|
||||||
Paging: pg,
|
Paging: pg,
|
||||||
AddrStr: address,
|
AddrStr: address,
|
||||||
BalanceSat: (*Amount)(&ba.BalanceSat),
|
BalanceSat: (*Amount)(&ba.BalanceSat),
|
||||||
TotalReceivedSat: (*Amount)(totalReceived),
|
TotalReceivedSat: (*Amount)(totalReceived),
|
||||||
TotalSentSat: (*Amount)(totalSent),
|
TotalSentSat: (*Amount)(totalSent),
|
||||||
TxApperances: int(ba.Txs),
|
Txs: int(ba.Txs),
|
||||||
UnconfirmedBalanceSat: (*Amount)(&uBalSat),
|
NonTokenTxs: nonTokenTxs,
|
||||||
UnconfirmedTxApperances: len(txm),
|
UnconfirmedBalanceSat: (*Amount)(&uBalSat),
|
||||||
Transactions: txs,
|
UnconfirmedTxs: len(txm),
|
||||||
Txids: txids,
|
Transactions: txs,
|
||||||
Erc20Contract: erc20c,
|
Txids: txids,
|
||||||
Erc20Tokens: erc20t,
|
Tokens: tokens,
|
||||||
Nonce: nonce,
|
Erc20Contract: erc20c,
|
||||||
|
Nonce: nonce,
|
||||||
}
|
}
|
||||||
glog.Info("GetAddress ", address, " finished in ", time.Since(start))
|
glog.Info("GetAddress ", address, " finished in ", time.Since(start))
|
||||||
return r, nil
|
return r, nil
|
||||||
|
|||||||
@ -433,7 +433,7 @@ func httpTests_BitcoinType(t *testing.T, ts *httptest.Server) {
|
|||||||
status: http.StatusOK,
|
status: http.StatusOK,
|
||||||
contentType: "application/json; charset=utf-8",
|
contentType: "application/json; charset=utf-8",
|
||||||
body: []string{
|
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>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Transactions</td>
|
<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>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Nonce</td>
|
<td>Nonce</td>
|
||||||
<td class="data">{{$addr.Nonce}}</td>
|
<td class="data">{{$addr.Nonce}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{- if $addr.Erc20Tokens -}}
|
{{- if $addr.Tokens -}}
|
||||||
<tr>
|
<tr>
|
||||||
<td>ERC20 Tokens</td>
|
<td>ERC20 Tokens</td>
|
||||||
<td style="padding: 0;">
|
<td style="padding: 0;">
|
||||||
@ -34,11 +38,11 @@
|
|||||||
<th>Tokens</th>
|
<th>Tokens</th>
|
||||||
<th style="width: 15%;">Transfers</th>
|
<th style="width: 15%;">Transfers</th>
|
||||||
</tr>
|
</tr>
|
||||||
{{- range $et := $addr.Erc20Tokens -}}
|
{{- range $t := $addr.Tokens -}}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="data ellipsis"><a href="/address/{{$et.Contract}}">{{$et.Name}}</a></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 $et.BalanceSat $et.Decimals}} {{$et.Symbol}}</td>
|
<td class="data">{{formatAmountWithDecimals $t.BalanceSat $t.Decimals}} {{$t.Symbol}}</td>
|
||||||
<td class="data">{{$et.Transfers}}</td>
|
<td class="data">{{$t.Transfers}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
</tbody>
|
</tbody>
|
||||||
@ -62,7 +66,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>No. Transactions</td>
|
<td>No. Transactions</td>
|
||||||
<td class="data">{{$addr.TxApperances}}</td>
|
<td class="data">{{$addr.Txs}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
</tbody>
|
</tbody>
|
||||||
@ -76,7 +80,7 @@
|
|||||||
</script>
|
</script>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{- if $addr.UnconfirmedTxApperances -}}
|
{{- if $addr.UnconfirmedTxs -}}
|
||||||
<h3>Unconfirmed</h3>
|
<h3>Unconfirmed</h3>
|
||||||
<div class="data-div">
|
<div class="data-div">
|
||||||
<table class="table data-table">
|
<table class="table data-table">
|
||||||
@ -87,7 +91,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>No. Transactions</td>
|
<td>No. Transactions</td>
|
||||||
<td class="data">{{$addr.UnconfirmedTxApperances}}</td>
|
<td class="data">{{$addr.UnconfirmedTxs}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@ -99,10 +103,10 @@
|
|||||||
<option>All</option>
|
<option>All</option>
|
||||||
<option {{if eq $addr.Filter "inputs" -}} selected{{end}} value="inputs">Inputs</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>
|
<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>
|
<option {{if eq $addr.Filter "0" -}} selected{{end}} value="0">Non-contract</option>
|
||||||
{{- range $et := $addr.Erc20Tokens -}}
|
{{- range $t := $addr.Tokens -}}
|
||||||
<option {{if eq $addr.Filter $et.ContractIndex -}} selected{{end}} value="{{$et.ContractIndex}}">{{$et.Name}}</option>
|
<option {{if eq $addr.Filter $t.ContractIndex -}} selected{{end}} value="{{$t.ContractIndex}}">{{$t.Name}}</option>
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user