Store contract info in DB
This commit is contained in:
parent
2507e12057
commit
db91824dc3
98
api/types.go
98
api/types.go
@ -135,58 +135,40 @@ type Vout struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
// TokenType specifies type of token
|
||||
type TokenType string
|
||||
|
||||
// Token types
|
||||
const (
|
||||
// Ethereum token types
|
||||
ERC20TokenType TokenType = "ERC20"
|
||||
ERC771TokenType TokenType = "ERC721"
|
||||
ERC1155TokenType TokenType = "ERC1155"
|
||||
|
||||
// XPUBAddressTokenType is address derived from xpub
|
||||
XPUBAddressTokenType TokenType = "XPUBAddress"
|
||||
)
|
||||
|
||||
// TokenTypeMap maps bchain.TokenTransferType to TokenType
|
||||
// the map must match all bchain.TokenTransferTypes to avoid index out of range panic
|
||||
var TokenTypeMap []TokenType = []TokenType{ERC20TokenType, ERC771TokenType, ERC1155TokenType}
|
||||
|
||||
// TokenTransferValues contains values for ERC1155 contract
|
||||
type TokenTransferValues struct {
|
||||
// MultiTokenValue contains values for contract with id and value (like ERC1155)
|
||||
type MultiTokenValue 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:"-"`
|
||||
Type bchain.TokenTypeName `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
|
||||
MultiTokenValues []MultiTokenValue `json:"multiTokenValues,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"`
|
||||
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,omitempty"`
|
||||
Values []TokenTransferValues `json:"values,omitempty"`
|
||||
Type bchain.TokenTypeName `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,omitempty"`
|
||||
MultiTokenValues []MultiTokenValue `json:"multiTokenValues,omitempty"`
|
||||
}
|
||||
|
||||
type EthereumInternalTransfer struct {
|
||||
@ -290,22 +272,22 @@ type AddressFilter struct {
|
||||
// Address holds information about address and its transactions
|
||||
type Address struct {
|
||||
Paging
|
||||
AddrStr string `json:"address"`
|
||||
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"`
|
||||
InternalTxs int `json:"internalTxs,omitempty"`
|
||||
Transactions []*Tx `json:"transactions,omitempty"`
|
||||
Txids []string `json:"txids,omitempty"`
|
||||
Nonce string `json:"nonce,omitempty"`
|
||||
UsedTokens int `json:"usedTokens,omitempty"`
|
||||
Tokens []Token `json:"tokens,omitempty"`
|
||||
Erc20Contract *bchain.Erc20Contract `json:"erc20Contract,omitempty"`
|
||||
AddressAliases AddressAliasesMap `json:"addressAliases,omitempty"`
|
||||
AddrStr string `json:"address"`
|
||||
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"`
|
||||
InternalTxs int `json:"internalTxs,omitempty"`
|
||||
Transactions []*Tx `json:"transactions,omitempty"`
|
||||
Txids []string `json:"txids,omitempty"`
|
||||
Nonce string `json:"nonce,omitempty"`
|
||||
UsedTokens int `json:"usedTokens,omitempty"`
|
||||
Tokens []Token `json:"tokens,omitempty"`
|
||||
ContractInfo *bchain.ContractInfo `json:"contractInfo,omitempty"`
|
||||
AddressAliases AddressAliasesMap `json:"addressAliases,omitempty"`
|
||||
// helpers for explorer
|
||||
Filter string `json:"-"`
|
||||
XPubAddresses map[string]struct{} `json:"-"`
|
||||
|
||||
131
api/worker.go
131
api/worker.go
@ -151,7 +151,10 @@ func (w *Worker) getAddressAliases(addresses map[string]struct{}) AddressAliases
|
||||
}
|
||||
for a := range addresses {
|
||||
if w.chainType == bchain.ChainEthereumType {
|
||||
// TODO get contract name
|
||||
ci, err := w.db.GetContractInfoForAddress(a)
|
||||
if err == nil && ci != nil && ci.Name != "" {
|
||||
aliases[a] = AddressAlias{Type: "Contract", Alias: ci.Name}
|
||||
}
|
||||
}
|
||||
n := w.db.GetAddressAlias(a)
|
||||
if len(n) > 0 {
|
||||
@ -535,20 +538,28 @@ func (w *Worker) getEthereumTokensTransfers(transfers bchain.TokenTransfers, add
|
||||
glog.Errorf("GetAddrDescFromAddress error %v, contract %v", err, t.Contract)
|
||||
continue
|
||||
}
|
||||
erc20c, err := w.chain.EthereumTypeGetErc20ContractInfo(cd)
|
||||
typeName := bchain.EthereumTokenTypeMap[t.Type]
|
||||
contractInfo, err := w.db.GetContractInfo(cd, typeName)
|
||||
if err != nil {
|
||||
glog.Errorf("GetErc20ContractInfo error %v, contract %v", err, t.Contract)
|
||||
glog.Errorf("GetContractInfo error %v, contract %v", err, t.Contract)
|
||||
}
|
||||
if erc20c == nil {
|
||||
erc20c = &bchain.Erc20Contract{Name: t.Contract}
|
||||
if contractInfo == nil {
|
||||
glog.Warningf("Contract %v %v not found in DB", t.Contract, typeName)
|
||||
contractInfo, err = w.chain.GetContractInfo(cd)
|
||||
if err != nil {
|
||||
glog.Errorf("GetContractInfo from chain error %v, contract %v", err, t.Contract)
|
||||
}
|
||||
if contractInfo == nil {
|
||||
contractInfo = &bchain.ContractInfo{Name: t.Contract, Type: bchain.UnknownTokenType}
|
||||
}
|
||||
}
|
||||
var value *Amount
|
||||
var values []TokenTransferValues
|
||||
if t.Type == bchain.ERC1155 {
|
||||
values = make([]TokenTransferValues, len(t.IdValues))
|
||||
var values []MultiTokenValue
|
||||
if t.Type == bchain.MultiToken {
|
||||
values = make([]MultiTokenValue, len(t.MultiTokenValues))
|
||||
for j := range values {
|
||||
values[j].Id = (*Amount)(&t.IdValues[j].Id)
|
||||
values[j].Value = (*Amount)(&t.IdValues[j].Value)
|
||||
values[j].Id = (*Amount)(&t.MultiTokenValues[j].Id)
|
||||
values[j].Value = (*Amount)(&t.MultiTokenValues[j].Value)
|
||||
}
|
||||
} else {
|
||||
value = (*Amount)(&t.Value)
|
||||
@ -556,15 +567,15 @@ func (w *Worker) getEthereumTokensTransfers(transfers bchain.TokenTransfers, add
|
||||
aggregateAddress(addresses, t.From)
|
||||
aggregateAddress(addresses, t.To)
|
||||
tokens[i] = TokenTransfer{
|
||||
Type: TokenTypeMap[t.Type],
|
||||
Token: t.Contract,
|
||||
From: t.From,
|
||||
To: t.To,
|
||||
Value: value,
|
||||
Values: values,
|
||||
Decimals: erc20c.Decimals,
|
||||
Name: erc20c.Name,
|
||||
Symbol: erc20c.Symbol,
|
||||
Type: typeName,
|
||||
Token: t.Contract,
|
||||
From: t.From,
|
||||
To: t.To,
|
||||
Value: value,
|
||||
MultiTokenValues: values,
|
||||
Decimals: contractInfo.Decimals,
|
||||
Name: contractInfo.Name,
|
||||
Symbol: contractInfo.Symbol,
|
||||
}
|
||||
}
|
||||
return tokens
|
||||
@ -751,35 +762,41 @@ func computePaging(count, page, itemsOnPage int) (Paging, int, int, int) {
|
||||
}
|
||||
|
||||
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)
|
||||
typeName := bchain.EthereumTokenTypeMap[c.Type]
|
||||
ci, err := w.db.GetContractInfo(c.Contract, typeName)
|
||||
if err != nil {
|
||||
return nil, errors.Annotatef(err, "EthereumTypeGetErc20ContractInfo %v", c.Contract)
|
||||
return nil, errors.Annotatef(err, "GetContractInfo %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]
|
||||
glog.Warningf("Contract %v %v not found in DB", c.Contract, typeName)
|
||||
ci, err = w.chain.GetContractInfo(c.Contract)
|
||||
if err != nil {
|
||||
glog.Errorf("GetContractInfo from chain error %v, contract %v", err, c.Contract)
|
||||
}
|
||||
if ci == nil {
|
||||
ci = &bchain.ContractInfo{Type: bchain.UnknownTokenType}
|
||||
addresses, _, _ := w.chainParser.GetAddressesFromAddrDesc(c.Contract)
|
||||
if len(addresses) > 0 {
|
||||
ci.Contract = addresses[0]
|
||||
ci.Name = addresses[0]
|
||||
}
|
||||
validContract = false
|
||||
}
|
||||
validContract = false
|
||||
}
|
||||
|
||||
t := Token{
|
||||
Contract: ci.Contract,
|
||||
Name: ci.Name,
|
||||
Symbol: ci.Symbol,
|
||||
Type: typeName,
|
||||
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 {
|
||||
t.Type = ERC20TokenType
|
||||
if c.Type == bchain.FungibleToken {
|
||||
// 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 {
|
||||
@ -789,11 +806,6 @@ func (w *Worker) getEthereumContractBalance(addrDesc bchain.AddressDescriptor, i
|
||||
t.BalanceSat = (*Amount)(b)
|
||||
}
|
||||
} else {
|
||||
if c.Type == bchain.ERC721 {
|
||||
t.Type = ERC771TokenType
|
||||
} else {
|
||||
t.Type = ERC1155TokenType
|
||||
}
|
||||
if len(c.Ids) > 0 {
|
||||
ids := make([]Amount, len(c.Ids))
|
||||
for j := range ids {
|
||||
@ -801,13 +813,13 @@ func (w *Worker) getEthereumContractBalance(addrDesc bchain.AddressDescriptor, i
|
||||
}
|
||||
t.Ids = ids
|
||||
}
|
||||
if len(c.IdValues) > 0 {
|
||||
idValues := make([]TokenTransferValues, len(c.IdValues))
|
||||
if len(c.MultiTokenValues) > 0 {
|
||||
idValues := make([]MultiTokenValue, len(c.MultiTokenValues))
|
||||
for j := range idValues {
|
||||
idValues[j].Id = (*Amount)(&c.IdValues[j].Id)
|
||||
idValues[j].Value = (*Amount)(&c.IdValues[j].Value)
|
||||
idValues[j].Id = (*Amount)(&c.MultiTokenValues[j].Id)
|
||||
idValues[j].Value = (*Amount)(&c.MultiTokenValues[j].Value)
|
||||
}
|
||||
t.IdValues = idValues
|
||||
t.MultiTokenValues = idValues
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -819,18 +831,25 @@ func (w *Worker) getEthereumContractBalance(addrDesc bchain.AddressDescriptor, i
|
||||
func (w *Worker) getEthereumContractBalanceFromBlockchain(addrDesc, contract bchain.AddressDescriptor, details AccountDetails) (*Token, error) {
|
||||
var b *big.Int
|
||||
validContract := true
|
||||
ci, err := w.chain.EthereumTypeGetErc20ContractInfo(contract)
|
||||
ci, err := w.db.GetContractInfo(contract, "")
|
||||
if err != nil {
|
||||
return nil, errors.Annotatef(err, "EthereumTypeGetErc20ContractInfo %v", contract)
|
||||
return nil, errors.Annotatef(err, "GetContractInfo %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]
|
||||
glog.Warningf("Contract %v not found in DB", contract)
|
||||
ci, err = w.chain.GetContractInfo(contract)
|
||||
if err != nil {
|
||||
glog.Errorf("GetContractInfo from chain error %v, contract %v", err, contract)
|
||||
}
|
||||
if ci == nil {
|
||||
ci = &bchain.ContractInfo{Type: bchain.UnknownTokenType}
|
||||
addresses, _, _ := w.chainParser.GetAddressesFromAddrDesc(contract)
|
||||
if len(addresses) > 0 {
|
||||
ci.Contract = addresses[0]
|
||||
ci.Name = addresses[0]
|
||||
}
|
||||
validContract = false
|
||||
}
|
||||
validContract = false
|
||||
}
|
||||
// do not read contract balances etc in case of Basic option
|
||||
if details >= AccountDetailsTokenBalances && validContract {
|
||||
@ -843,7 +862,7 @@ func (w *Worker) getEthereumContractBalanceFromBlockchain(addrDesc, contract bch
|
||||
b = nil
|
||||
}
|
||||
return &Token{
|
||||
Type: ERC20TokenType,
|
||||
Type: ci.Type,
|
||||
BalanceSat: (*Amount)(b),
|
||||
Contract: ci.Contract,
|
||||
Name: ci.Name,
|
||||
@ -854,11 +873,11 @@ func (w *Worker) getEthereumContractBalanceFromBlockchain(addrDesc, contract bch
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescriptor, details AccountDetails, filter *AddressFilter) (*db.AddrBalance, []Token, *bchain.Erc20Contract, uint64, int, int, int, error) {
|
||||
func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescriptor, details AccountDetails, filter *AddressFilter) (*db.AddrBalance, []Token, *bchain.ContractInfo, uint64, int, int, int, error) {
|
||||
var (
|
||||
ba *db.AddrBalance
|
||||
tokens []Token
|
||||
ci *bchain.Erc20Contract
|
||||
ci *bchain.ContractInfo
|
||||
n uint64
|
||||
nonContractTxs int
|
||||
internalTxs int
|
||||
@ -912,7 +931,7 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
|
||||
}
|
||||
tokens = tokens[:j]
|
||||
}
|
||||
ci, err = w.chain.EthereumTypeGetErc20ContractInfo(addrDesc)
|
||||
ci, err = w.db.GetContractInfo(addrDesc, "")
|
||||
if err != nil {
|
||||
return nil, nil, nil, 0, 0, 0, 0, err
|
||||
}
|
||||
@ -1043,7 +1062,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco
|
||||
var (
|
||||
ba *db.AddrBalance
|
||||
tokens []Token
|
||||
erc20c *bchain.Erc20Contract
|
||||
contractInfo *bchain.ContractInfo
|
||||
txm []string
|
||||
txs []*Tx
|
||||
txids []string
|
||||
@ -1062,7 +1081,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco
|
||||
}
|
||||
if w.chainType == bchain.ChainEthereumType {
|
||||
var n uint64
|
||||
ba, tokens, erc20c, n, nonTokenTxs, internalTxs, totalResults, err = w.getEthereumTypeAddressBalances(addrDesc, option, filter)
|
||||
ba, tokens, contractInfo, n, nonTokenTxs, internalTxs, totalResults, err = w.getEthereumTypeAddressBalances(addrDesc, option, filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -1173,7 +1192,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco
|
||||
Transactions: txs,
|
||||
Txids: txids,
|
||||
Tokens: tokens,
|
||||
Erc20Contract: erc20c,
|
||||
ContractInfo: contractInfo,
|
||||
Nonce: nonce,
|
||||
AddressAliases: w.getAddressAliases(addresses),
|
||||
}
|
||||
|
||||
@ -266,7 +266,7 @@ func (w *Worker) tokenFromXpubAddress(data *xpubData, ad *xpubAddress, changeInd
|
||||
}
|
||||
}
|
||||
return Token{
|
||||
Type: XPUBAddressTokenType,
|
||||
Type: bchain.XPUBAddressTokenType,
|
||||
Name: address,
|
||||
Decimals: w.chainParser.AmountDecimals(),
|
||||
BalanceSat: (*Amount)(balance),
|
||||
|
||||
@ -54,8 +54,8 @@ func (b *BaseChain) EthereumTypeEstimateGas(params map[string]interface{}) (uint
|
||||
return 0, errors.New("Not supported")
|
||||
}
|
||||
|
||||
// EthereumTypeGetErc20ContractInfo is not supported
|
||||
func (b *BaseChain) EthereumTypeGetErc20ContractInfo(contractDesc AddressDescriptor) (*Erc20Contract, error) {
|
||||
// GetContractInfo is not supported
|
||||
func (b *BaseChain) GetContractInfo(contractDesc AddressDescriptor) (*ContractInfo, error) {
|
||||
return nil, errors.New("Not supported")
|
||||
}
|
||||
|
||||
|
||||
@ -321,13 +321,13 @@ func (c *blockChainWithMetrics) EthereumTypeEstimateGas(params map[string]interf
|
||||
return c.b.EthereumTypeEstimateGas(params)
|
||||
}
|
||||
|
||||
func (c *blockChainWithMetrics) EthereumTypeGetErc20ContractInfo(contractDesc bchain.AddressDescriptor) (v *bchain.Erc20Contract, err error) {
|
||||
defer func(s time.Time) { c.observeRPCLatency("EthereumTypeGetErc20ContractInfo", s, err) }(time.Now())
|
||||
return c.b.EthereumTypeGetErc20ContractInfo(contractDesc)
|
||||
func (c *blockChainWithMetrics) GetContractInfo(contractDesc bchain.AddressDescriptor) (v *bchain.ContractInfo, err error) {
|
||||
defer func(s time.Time) { c.observeRPCLatency("GetContractInfo", s, err) }(time.Now())
|
||||
return c.b.GetContractInfo(contractDesc)
|
||||
}
|
||||
|
||||
func (c *blockChainWithMetrics) EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc bchain.AddressDescriptor) (v *big.Int, err error) {
|
||||
defer func(s time.Time) { c.observeRPCLatency("EthereumTypeGetErc20ContractInfo", s, err) }(time.Now())
|
||||
defer func(s time.Time) { c.observeRPCLatency("EthereumTypeGetErc20ContractBalance", s, err) }(time.Now())
|
||||
return c.b.EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc)
|
||||
}
|
||||
|
||||
|
||||
@ -4,10 +4,8 @@ import (
|
||||
"context"
|
||||
"math/big"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
ethcommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/golang/glog"
|
||||
"github.com/juju/errors"
|
||||
"github.com/trezor/blockbook/bchain"
|
||||
)
|
||||
@ -28,9 +26,6 @@ const contractSymbolSignature = "0x95d89b41"
|
||||
const contractDecimalsSignature = "0x313ce567"
|
||||
const contractBalanceOf = "0x70a08231"
|
||||
|
||||
var cachedContracts = make(map[string]*bchain.Erc20Contract)
|
||||
var cachedContractsMux sync.Mutex
|
||||
|
||||
func addressFromPaddedHex(s string) (string, error) {
|
||||
var t big.Int
|
||||
var ok bool
|
||||
@ -48,16 +43,16 @@ func addressFromPaddedHex(s string) (string, error) {
|
||||
|
||||
func processTransferEvent(l *bchain.RpcLog) (*bchain.TokenTransfer, error) {
|
||||
tl := len(l.Topics)
|
||||
var ttt bchain.TokenTransferType
|
||||
var ttt bchain.TokenType
|
||||
var value big.Int
|
||||
if tl == 3 {
|
||||
ttt = bchain.ERC20
|
||||
ttt = bchain.FungibleToken
|
||||
_, ok := value.SetString(l.Data, 0)
|
||||
if !ok {
|
||||
return nil, errors.New("ERC20 log Data is not a number")
|
||||
}
|
||||
} else if tl == 4 {
|
||||
ttt = bchain.ERC721
|
||||
ttt = bchain.NonFungibleToken
|
||||
_, ok := value.SetString(l.Topics[3], 0)
|
||||
if !ok {
|
||||
return nil, errors.New("ERC721 log Topics[3] is not a number")
|
||||
@ -105,11 +100,11 @@ func processERC1155TransferSingleEvent(l *bchain.RpcLog) (*bchain.TokenTransfer,
|
||||
return nil, errors.New("ERC1155 log Data value is not a number")
|
||||
}
|
||||
return &bchain.TokenTransfer{
|
||||
Type: bchain.ERC1155,
|
||||
Contract: EIP55AddressFromAddress(l.Address),
|
||||
From: EIP55AddressFromAddress(from),
|
||||
To: EIP55AddressFromAddress(to),
|
||||
IdValues: []bchain.TokenTransferIdValue{{Id: id, Value: value}},
|
||||
Type: bchain.MultiToken,
|
||||
Contract: EIP55AddressFromAddress(l.Address),
|
||||
From: EIP55AddressFromAddress(from),
|
||||
To: EIP55AddressFromAddress(to),
|
||||
MultiTokenValues: []bchain.MultiTokenValue{{Id: id, Value: value}},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -150,7 +145,7 @@ func processERC1155TransferBatchEvent(l *bchain.RpcLog) (*bchain.TokenTransfer,
|
||||
if countIds != countValues {
|
||||
return nil, errors.New("ERC1155 TransferBatch, count values and ids does not match")
|
||||
}
|
||||
idValues := make([]bchain.TokenTransferIdValue, countValues)
|
||||
idValues := make([]bchain.MultiTokenValue, countValues)
|
||||
for i := 0; i < countValues; i++ {
|
||||
var id, value big.Int
|
||||
o := offsetIds + 64 + 64*i
|
||||
@ -163,14 +158,14 @@ func processERC1155TransferBatchEvent(l *bchain.RpcLog) (*bchain.TokenTransfer,
|
||||
if !ok {
|
||||
return nil, errors.New("ERC1155 log Data value is not a number")
|
||||
}
|
||||
idValues[i] = bchain.TokenTransferIdValue{Id: id, Value: value}
|
||||
idValues[i] = bchain.MultiTokenValue{Id: id, Value: value}
|
||||
}
|
||||
return &bchain.TokenTransfer{
|
||||
Type: bchain.ERC1155,
|
||||
Contract: EIP55AddressFromAddress(l.Address),
|
||||
From: EIP55AddressFromAddress(from),
|
||||
To: EIP55AddressFromAddress(to),
|
||||
IdValues: idValues,
|
||||
Type: bchain.MultiToken,
|
||||
Contract: EIP55AddressFromAddress(l.Address),
|
||||
From: EIP55AddressFromAddress(from),
|
||||
To: EIP55AddressFromAddress(to),
|
||||
MultiTokenValues: idValues,
|
||||
}, nil
|
||||
}
|
||||
func contractGetTransfersFromLog(logs []*bchain.RpcLog) (bchain.TokenTransfers, error) {
|
||||
@ -214,7 +209,7 @@ func contractGetTransfersFromTx(tx *bchain.RpcTransaction) (bchain.TokenTransfer
|
||||
return nil, errors.New("Data is not a number")
|
||||
}
|
||||
r = append(r, &bchain.TokenTransfer{
|
||||
Type: bchain.ERC20,
|
||||
Type: bchain.FungibleToken,
|
||||
Contract: EIP55AddressFromAddress(tx.To),
|
||||
From: EIP55AddressFromAddress(tx.From),
|
||||
To: EIP55AddressFromAddress(to),
|
||||
@ -238,7 +233,7 @@ func contractGetTransfersFromTx(tx *bchain.RpcTransaction) (bchain.TokenTransfer
|
||||
return nil, errors.New("Data is not a number")
|
||||
}
|
||||
r = append(r, &bchain.TokenTransfer{
|
||||
Type: bchain.ERC721,
|
||||
Type: bchain.NonFungibleToken,
|
||||
Contract: EIP55AddressFromAddress(tx.To),
|
||||
From: EIP55AddressFromAddress(from),
|
||||
To: EIP55AddressFromAddress(to),
|
||||
@ -262,56 +257,52 @@ func (b *EthereumRPC) ethCall(data, to string) (string, error) {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// EthereumTypeGetErc20ContractInfo returns information about ERC20 contract
|
||||
func (b *EthereumRPC) EthereumTypeGetErc20ContractInfo(contractDesc bchain.AddressDescriptor) (*bchain.Erc20Contract, error) {
|
||||
cds := string(contractDesc)
|
||||
cachedContractsMux.Lock()
|
||||
contract, found := cachedContracts[cds]
|
||||
cachedContractsMux.Unlock()
|
||||
if !found {
|
||||
address := EIP55Address(contractDesc)
|
||||
data, err := b.ethCall(contractNameSignature, address)
|
||||
if err != nil {
|
||||
// ignore the error from the eth_call - since geth v1.9.15 they changed the behavior
|
||||
// and returning error "execution reverted" for some non contract addresses
|
||||
// https://github.com/ethereum/go-ethereum/issues/21249#issuecomment-648647672
|
||||
glog.Warning(errors.Annotatef(err, "erc20NameSignature %v", address))
|
||||
return nil, nil
|
||||
// return nil, errors.Annotatef(err, "erc20NameSignature %v", address)
|
||||
}
|
||||
name := parseSimpleStringProperty(data)
|
||||
if name != "" {
|
||||
data, err = b.ethCall(contractSymbolSignature, address)
|
||||
if err != nil {
|
||||
glog.Warning(errors.Annotatef(err, "erc20SymbolSignature %v", address))
|
||||
return nil, nil
|
||||
// return nil, errors.Annotatef(err, "erc20SymbolSignature %v", address)
|
||||
}
|
||||
symbol := parseSimpleStringProperty(data)
|
||||
data, err = b.ethCall(contractDecimalsSignature, address)
|
||||
if err != nil {
|
||||
glog.Warning(errors.Annotatef(err, "erc20DecimalsSignature %v", address))
|
||||
// return nil, errors.Annotatef(err, "erc20DecimalsSignature %v", address)
|
||||
}
|
||||
contract = &bchain.Erc20Contract{
|
||||
Contract: address,
|
||||
Name: name,
|
||||
Symbol: symbol,
|
||||
}
|
||||
d := parseSimpleNumericProperty(data)
|
||||
if d != nil {
|
||||
contract.Decimals = int(uint8(d.Uint64()))
|
||||
} else {
|
||||
contract.Decimals = EtherAmountDecimalPoint
|
||||
}
|
||||
} else {
|
||||
contract = nil
|
||||
}
|
||||
cachedContractsMux.Lock()
|
||||
cachedContracts[cds] = contract
|
||||
cachedContractsMux.Unlock()
|
||||
func (b *EthereumRPC) fetchContractInfo(address string) (*bchain.ContractInfo, error) {
|
||||
var contract bchain.ContractInfo
|
||||
data, err := b.ethCall(contractNameSignature, address)
|
||||
if err != nil {
|
||||
// ignore the error from the eth_call - since geth v1.9.15 they changed the behavior
|
||||
// and returning error "execution reverted" for some non contract addresses
|
||||
// https://github.com/ethereum/go-ethereum/issues/21249#issuecomment-648647672
|
||||
// glog.Warning(errors.Annotatef(err, "Contract NameSignature %v", address))
|
||||
return nil, nil
|
||||
// return nil, errors.Annotatef(err, "erc20NameSignature %v", address)
|
||||
}
|
||||
return contract, nil
|
||||
name := parseSimpleStringProperty(data)
|
||||
if name != "" {
|
||||
data, err = b.ethCall(contractSymbolSignature, address)
|
||||
if err != nil {
|
||||
// glog.Warning(errors.Annotatef(err, "Contract SymbolSignature %v", address))
|
||||
return nil, nil
|
||||
// return nil, errors.Annotatef(err, "erc20SymbolSignature %v", address)
|
||||
}
|
||||
symbol := parseSimpleStringProperty(data)
|
||||
data, _ = b.ethCall(contractDecimalsSignature, address)
|
||||
// if err != nil {
|
||||
// glog.Warning(errors.Annotatef(err, "Contract DecimalsSignature %v", address))
|
||||
// // return nil, errors.Annotatef(err, "erc20DecimalsSignature %v", address)
|
||||
// }
|
||||
contract = bchain.ContractInfo{
|
||||
Contract: address,
|
||||
Name: name,
|
||||
Symbol: symbol,
|
||||
}
|
||||
d := parseSimpleNumericProperty(data)
|
||||
if d != nil {
|
||||
contract.Decimals = int(uint8(d.Uint64()))
|
||||
} else {
|
||||
contract.Decimals = EtherAmountDecimalPoint
|
||||
}
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
return &contract, nil
|
||||
}
|
||||
|
||||
// GetContractInfo returns information about a contract
|
||||
func (b *EthereumRPC) GetContractInfo(contractDesc bchain.AddressDescriptor) (*bchain.ContractInfo, error) {
|
||||
address := EIP55Address(contractDesc)
|
||||
return b.fetchContractInfo(address)
|
||||
}
|
||||
|
||||
// EthereumTypeGetErc20ContractBalance returns balance of ERC20 contract for given address
|
||||
|
||||
@ -133,7 +133,7 @@ func Test_contractGetTransfersFromLog(t *testing.T) {
|
||||
},
|
||||
want: bchain.TokenTransfers{
|
||||
{
|
||||
Type: bchain.ERC721,
|
||||
Type: bchain.NonFungibleToken,
|
||||
Contract: "0x5689b918D34C038901870105A6C7fc24744D31eB",
|
||||
From: "0x0a206d4d5ff79cb5069def7fe3598421cff09391",
|
||||
To: "0x6a016d7eec560549ffa0fbdb7f15c2b27302087f",
|
||||
@ -171,11 +171,11 @@ func Test_contractGetTransfersFromLog(t *testing.T) {
|
||||
},
|
||||
want: bchain.TokenTransfers{
|
||||
{
|
||||
Type: bchain.ERC1155,
|
||||
Contract: "0x6Fd712E3A5B556654044608F9129040A4839E36c",
|
||||
From: "0xa3950b823cb063dd9afc0d27f35008b805b3ed53",
|
||||
To: "0x4392faf3bb96b5694ecc6ef64726f61cdd4bb0ec",
|
||||
IdValues: []bchain.TokenTransferIdValue{{Id: *big.NewInt(150), Value: *big.NewInt(0x11)}},
|
||||
Type: bchain.MultiToken,
|
||||
Contract: "0x6Fd712E3A5B556654044608F9129040A4839E36c",
|
||||
From: "0xa3950b823cb063dd9afc0d27f35008b805b3ed53",
|
||||
To: "0x4392faf3bb96b5694ecc6ef64726f61cdd4bb0ec",
|
||||
MultiTokenValues: []bchain.MultiTokenValue{{Id: *big.NewInt(150), Value: *big.NewInt(0x11)}},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -195,11 +195,11 @@ func Test_contractGetTransfersFromLog(t *testing.T) {
|
||||
},
|
||||
want: bchain.TokenTransfers{
|
||||
{
|
||||
Type: bchain.ERC1155,
|
||||
Type: bchain.MultiToken,
|
||||
Contract: "0x6c42c26a081c2f509f8bb68fb7ac3062311ccfb7",
|
||||
From: "0x0000000000000000000000000000000000000000",
|
||||
To: "0x5dc6288b35e0807a3d6feb89b3a2ff4ab773168e",
|
||||
IdValues: []bchain.TokenTransferIdValue{
|
||||
MultiTokenValues: []bchain.MultiTokenValue{
|
||||
{Id: *big.NewInt(1776), Value: *big.NewInt(1)},
|
||||
{Id: *big.NewInt(1898), Value: *big.NewInt(10)},
|
||||
},
|
||||
@ -247,7 +247,7 @@ func Test_contractGetTransfersFromTx(t *testing.T) {
|
||||
args: (b1.Txs[1].CoinSpecificData.(bchain.EthereumSpecificData)).Tx,
|
||||
want: bchain.TokenTransfers{
|
||||
{
|
||||
Type: bchain.ERC20,
|
||||
Type: bchain.FungibleToken,
|
||||
Contract: "0x4af4114f73d1c1c903ac9e0361b379d1291808a2",
|
||||
From: "0x20cd153de35d469ba46127a0c8f18626b59a256a",
|
||||
To: "0x555ee11fbddc0e49a9bab358a8941ad95ffdb48f",
|
||||
@ -260,7 +260,7 @@ func Test_contractGetTransfersFromTx(t *testing.T) {
|
||||
args: (b2.Txs[2].CoinSpecificData.(bchain.EthereumSpecificData)).Tx,
|
||||
want: bchain.TokenTransfers{
|
||||
{
|
||||
Type: bchain.ERC721,
|
||||
Type: bchain.NonFungibleToken,
|
||||
Contract: "0xcda9fc258358ecaa88845f19af595e908bb7efe9",
|
||||
From: "0x837e3f699d85a4b0b99894567e9233dfb1dcb081",
|
||||
To: "0x7b62eb7fe80350dc7ec945c0b73242cb9877fb1b",
|
||||
|
||||
@ -39,8 +39,8 @@ func parseSimpleStringProperty(data string) string {
|
||||
if len(data) > 128 {
|
||||
n := parseSimpleNumericProperty(data[64:128])
|
||||
if n != nil {
|
||||
l := n.Uint64()
|
||||
if l > 0 && 2*int(l) <= len(data)-128 {
|
||||
l := n.Int64()
|
||||
if l > 0 && int(l) <= ((len(data)-128)>>1) {
|
||||
b, err := hex.DecodeString(data[128 : 128+2*l])
|
||||
if err == nil {
|
||||
return string(b)
|
||||
|
||||
@ -45,6 +45,11 @@ func Test_parseSimpleStringProperty(t *testing.T) {
|
||||
args: "0x2234880850896048596206002535425366538144616734015984380565810000",
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "garbage",
|
||||
args: "6080604052600436106100225760003560e01c80630cbcae701461003957610031565b366100315761002f610077565b005b61002f610077565b34801561004557600080fd5b5061004e61014e565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b7f000000000000000000000000000000000000000000000000000000000000000061011c565b60043560601b60601c6bca11c0de15dead10cced00006000195460a01c036100e9577f696d706c6f63000000000000000000000000000000000000000000000000000060005260206000fd5b8060001955005b60405136810160405236600082376000803683600019545af43d6000833e80610117573d82fd5b503d81f35b80330361014357602436036101435763ca11c0de60003560e01c036101435761014361009d565b61014b6100f0565b50565b600073ffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff541660005260206000f3fea2646970667358221220f27ad3f3b75609baa5d26d65ec1001c4a59f38e89088d6b47517c1cd1faf22ab64736f6c634300080d0033",
|
||||
want: "",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
@ -537,23 +537,37 @@ type rpcTraceResult struct {
|
||||
Result rpcCallTrace `json:"result"`
|
||||
}
|
||||
|
||||
func (b *EthereumRPC) processCallTrace(call *rpcCallTrace, d *bchain.EthereumInternalData) {
|
||||
func (b *EthereumRPC) getCreationContractInfo(contract string, height uint32) *bchain.ContractInfo {
|
||||
ci, err := b.fetchContractInfo(contract)
|
||||
if ci == nil || err != nil {
|
||||
ci = &bchain.ContractInfo{
|
||||
Contract: contract,
|
||||
}
|
||||
}
|
||||
ci.Type = bchain.UnknownTokenType
|
||||
ci.CreatedInBlock = height
|
||||
return ci
|
||||
}
|
||||
|
||||
func (b *EthereumRPC) processCallTrace(call *rpcCallTrace, d *bchain.EthereumInternalData, contracts []bchain.ContractInfo, blockHeight uint32) []bchain.ContractInfo {
|
||||
value, err := hexutil.DecodeBig(call.Value)
|
||||
if call.Type == "CREATE" {
|
||||
d.Transfers = append(d.Transfers, bchain.EthereumInternalTransfer{
|
||||
Type: bchain.CREATE,
|
||||
Value: *value,
|
||||
From: call.From,
|
||||
To: call.To,
|
||||
To: call.To, // new contract address
|
||||
})
|
||||
contracts = append(contracts, *b.getCreationContractInfo(call.To, blockHeight))
|
||||
|
||||
} else if call.Type == "SELFDESTRUCT" {
|
||||
d.Transfers = append(d.Transfers, bchain.EthereumInternalTransfer{
|
||||
Type: bchain.SELFDESTRUCT,
|
||||
Value: *value,
|
||||
From: call.From,
|
||||
From: call.From, // destroyed contract address
|
||||
To: call.To,
|
||||
})
|
||||
contracts = append(contracts, bchain.ContractInfo{Contract: call.From, DestructedInBlock: blockHeight})
|
||||
} else if err == nil && (value.BitLen() > 0 || b.ChainConfig.ProcessZeroInternalTransactions) {
|
||||
d.Transfers = append(d.Transfers, bchain.EthereumInternalTransfer{
|
||||
Value: *value,
|
||||
@ -565,13 +579,15 @@ func (b *EthereumRPC) processCallTrace(call *rpcCallTrace, d *bchain.EthereumInt
|
||||
d.Error = call.Error
|
||||
}
|
||||
for i := range call.Calls {
|
||||
b.processCallTrace(&call.Calls[i], d)
|
||||
contracts = b.processCallTrace(&call.Calls[i], d, contracts, blockHeight)
|
||||
}
|
||||
return contracts
|
||||
}
|
||||
|
||||
// getInternalDataForBlock fetches debug trace using callTracer, extracts internal transfers and creations and destructions of contracts
|
||||
func (b *EthereumRPC) getInternalDataForBlock(blockHash string, transactions []bchain.RpcTransaction) ([]bchain.EthereumInternalData, error) {
|
||||
func (b *EthereumRPC) getInternalDataForBlock(blockHash string, blockHeight uint32, transactions []bchain.RpcTransaction) ([]bchain.EthereumInternalData, []bchain.ContractInfo, error) {
|
||||
data := make([]bchain.EthereumInternalData, len(transactions))
|
||||
contracts := make([]bchain.ContractInfo, 0)
|
||||
if ProcessInternalTransactions {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
|
||||
defer cancel()
|
||||
@ -579,11 +595,11 @@ func (b *EthereumRPC) getInternalDataForBlock(blockHash string, transactions []b
|
||||
err := b.rpc.CallContext(ctx, &trace, "debug_traceBlockByHash", blockHash, map[string]interface{}{"tracer": "callTracer"})
|
||||
if err != nil {
|
||||
glog.Error("debug_traceBlockByHash block ", blockHash, ", error ", err)
|
||||
return data, err
|
||||
return data, contracts, err
|
||||
}
|
||||
if len(trace) != len(data) {
|
||||
glog.Error("debug_traceBlockByHash block ", blockHash, ", error: trace length does not match block length ", len(trace), "!=", len(data))
|
||||
return data, err
|
||||
return data, contracts, err
|
||||
}
|
||||
for i, result := range trace {
|
||||
r := &result.Result
|
||||
@ -591,11 +607,12 @@ func (b *EthereumRPC) getInternalDataForBlock(blockHash string, transactions []b
|
||||
if r.Type == "CREATE" {
|
||||
d.Type = bchain.CREATE
|
||||
d.Contract = r.To
|
||||
contracts = append(contracts, *b.getCreationContractInfo(d.Contract, blockHeight))
|
||||
} else if r.Type == "SELFDESTRUCT" {
|
||||
d.Type = bchain.SELFDESTRUCT
|
||||
}
|
||||
for j := range r.Calls {
|
||||
b.processCallTrace(&r.Calls[j], d)
|
||||
contracts = b.processCallTrace(&r.Calls[j], d, contracts, blockHeight)
|
||||
}
|
||||
if r.Error != "" {
|
||||
baseError := PackInternalTransactionError(r.Error)
|
||||
@ -620,7 +637,7 @@ func (b *EthereumRPC) getInternalDataForBlock(blockHash string, transactions []b
|
||||
}
|
||||
}
|
||||
}
|
||||
return data, nil
|
||||
return data, contracts, nil
|
||||
}
|
||||
|
||||
// GetBlock returns block with given hash or height, hash has precedence if both passed
|
||||
@ -642,15 +659,16 @@ func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error
|
||||
return nil, errors.Annotatef(err, "hash %v, height %v", hash, height)
|
||||
}
|
||||
// get block events
|
||||
// TODO - could be possibly done in parallel to getInternalDataForBlock
|
||||
logs, ens, err := b.processEventsForBlock(head.Number)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// error fetching internal data does not stop the block processing
|
||||
var blockSpecificData *bchain.EthereumBlockSpecificData
|
||||
internalData, err := b.getInternalDataForBlock(head.Hash, body.Transactions)
|
||||
internalData, contracts, err := b.getInternalDataForBlock(head.Hash, bbh.Height, body.Transactions)
|
||||
// pass internalData error and ENS records in blockSpecificData to be stored
|
||||
if err != nil || len(ens) > 0 {
|
||||
if err != nil || len(ens) > 0 || len(contracts) > 0 {
|
||||
blockSpecificData = &bchain.EthereumBlockSpecificData{}
|
||||
if err != nil {
|
||||
blockSpecificData.InternalDataError = err.Error()
|
||||
@ -658,7 +676,11 @@ func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error
|
||||
}
|
||||
if len(ens) > 0 {
|
||||
blockSpecificData.AddressAliasRecords = ens
|
||||
glog.Info("ENS", ens)
|
||||
// glog.Info("ENS", ens)
|
||||
}
|
||||
if len(contracts) > 0 {
|
||||
blockSpecificData.Contracts = contracts
|
||||
// glog.Info("Contracts", contracts)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -113,6 +113,27 @@ type MempoolTx struct {
|
||||
CoinSpecificData interface{} `json:"-"`
|
||||
}
|
||||
|
||||
// TokenType - type of token
|
||||
type TokenType int
|
||||
|
||||
// TokenType enumeration
|
||||
const (
|
||||
FungibleToken = TokenType(iota) // ERC20
|
||||
NonFungibleToken // ERC721
|
||||
MultiToken // ERC1155
|
||||
)
|
||||
|
||||
// TokenTypeName specifies type of token
|
||||
type TokenTypeName string
|
||||
|
||||
// Token types
|
||||
const (
|
||||
UnknownTokenType TokenTypeName = ""
|
||||
|
||||
// XPUBAddressTokenType is address derived from xpub
|
||||
XPUBAddressTokenType TokenTypeName = "XPUBAddress"
|
||||
)
|
||||
|
||||
// TokenTransfers is array of TokenTransfer
|
||||
type TokenTransfers []*TokenTransfer
|
||||
|
||||
@ -286,13 +307,13 @@ type BlockChain interface {
|
||||
EstimateFee(blocks int) (big.Int, error)
|
||||
SendRawTransaction(tx string) (string, error)
|
||||
GetMempoolEntry(txid string) (*MempoolEntry, error)
|
||||
GetContractInfo(contractDesc AddressDescriptor) (*ContractInfo, error)
|
||||
// parser
|
||||
GetChainParser() BlockChainParser
|
||||
// EthereumType specific
|
||||
EthereumTypeGetBalance(addrDesc AddressDescriptor) (*big.Int, error)
|
||||
EthereumTypeGetNonce(addrDesc AddressDescriptor) (uint64, error)
|
||||
EthereumTypeEstimateGas(params map[string]interface{}) (uint64, error)
|
||||
EthereumTypeGetErc20ContractInfo(contractDesc AddressDescriptor) (*Erc20Contract, error)
|
||||
EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc AddressDescriptor) (*big.Int, error)
|
||||
}
|
||||
|
||||
|
||||
@ -47,16 +47,6 @@ const (
|
||||
SELFDESTRUCT
|
||||
)
|
||||
|
||||
// TokenTransferType - type of token transfer
|
||||
type TokenTransferType int
|
||||
|
||||
// TokenTransferType enumeration
|
||||
const (
|
||||
ERC20 = TokenTransferType(iota)
|
||||
ERC721
|
||||
ERC1155
|
||||
)
|
||||
|
||||
// EthereumInternalTransaction contains internal transfers
|
||||
type EthereumInternalData struct {
|
||||
Type EthereumInternalTransactionType `json:"type"`
|
||||
@ -65,27 +55,41 @@ type EthereumInternalData struct {
|
||||
Error string
|
||||
}
|
||||
|
||||
// Erc20Contract contains info about ERC20 contract
|
||||
type Erc20Contract struct {
|
||||
Contract string `json:"contract"`
|
||||
Name string `json:"name"`
|
||||
Symbol string `json:"symbol"`
|
||||
Decimals int `json:"decimals"`
|
||||
// ContractInfo contains info about ERC20 contract
|
||||
type ContractInfo struct {
|
||||
Type TokenTypeName `json:"type"`
|
||||
Contract string `json:"contract"`
|
||||
Name string `json:"name"`
|
||||
Symbol string `json:"symbol"`
|
||||
Decimals int `json:"decimals"`
|
||||
CreatedInBlock uint32 `json:"createdInBlock,omitempty"`
|
||||
DestructedInBlock uint32 `json:"destructedInBlock,omitempty"`
|
||||
}
|
||||
|
||||
type TokenTransferIdValue struct {
|
||||
// Ethereum token type names
|
||||
const (
|
||||
ERC20TokenType TokenTypeName = "ERC20"
|
||||
ERC771TokenType TokenTypeName = "ERC721"
|
||||
ERC1155TokenType TokenTypeName = "ERC1155"
|
||||
)
|
||||
|
||||
// EthereumTokenTypeMap maps bchain.TokenType to TokenTypeName
|
||||
// the map must match all bchain.TokenType to avoid index out of range panic
|
||||
var EthereumTokenTypeMap []TokenTypeName = []TokenTypeName{ERC20TokenType, ERC771TokenType, ERC1155TokenType}
|
||||
|
||||
type MultiTokenValue struct {
|
||||
Id big.Int
|
||||
Value big.Int
|
||||
}
|
||||
|
||||
// TokenTransfer contains a single ERC20/ERC721/ERC1155 token transfer
|
||||
// TokenTransfer contains a single token transfer
|
||||
type TokenTransfer struct {
|
||||
Type TokenTransferType
|
||||
Contract string
|
||||
From string
|
||||
To string
|
||||
Value big.Int
|
||||
IdValues []TokenTransferIdValue
|
||||
Type TokenType
|
||||
Contract string
|
||||
From string
|
||||
To string
|
||||
Value big.Int
|
||||
MultiTokenValues []MultiTokenValue
|
||||
}
|
||||
|
||||
// RpcTransaction is returned by eth_getTransactionByHash
|
||||
@ -138,4 +142,5 @@ type AddressAliasRecord struct {
|
||||
type EthereumBlockSpecificData struct {
|
||||
InternalDataError string
|
||||
AddressAliasRecords []AddressAliasRecord
|
||||
Contracts []ContractInfo
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import (
|
||||
"math/big"
|
||||
"sync"
|
||||
|
||||
vlq "github.com/bsm/go-vlq"
|
||||
"github.com/flier/gorocksdb"
|
||||
"github.com/golang/glog"
|
||||
"github.com/juju/errors"
|
||||
@ -18,12 +19,12 @@ const ContractIndexOffset = 2
|
||||
|
||||
// AddrContract is Contract address with number of transactions done by given address
|
||||
type AddrContract struct {
|
||||
Type bchain.TokenTransferType
|
||||
Contract bchain.AddressDescriptor
|
||||
Txs uint
|
||||
Value big.Int // single value of ERC20
|
||||
Ids []big.Int // multiple ERC721 tokens
|
||||
IdValues []bchain.TokenTransferIdValue // multiple ERC1155 tokens
|
||||
Type bchain.TokenType
|
||||
Contract bchain.AddressDescriptor
|
||||
Txs uint
|
||||
Value big.Int // single value of ERC20
|
||||
Ids []big.Int // multiple ERC721 tokens
|
||||
MultiTokenValues []bchain.MultiTokenValue // multiple ERC1155 tokens
|
||||
}
|
||||
|
||||
// AddrContracts contains number of transactions and contracts for an address
|
||||
@ -48,10 +49,10 @@ func packAddrContracts(acs *AddrContracts) []byte {
|
||||
buf = append(buf, ac.Contract...)
|
||||
l = packVaruint(uint(ac.Type)+ac.Txs<<2, varBuf)
|
||||
buf = append(buf, varBuf[:l]...)
|
||||
if ac.Type == bchain.ERC20 {
|
||||
if ac.Type == bchain.FungibleToken {
|
||||
l = packBigint(&ac.Value, varBuf)
|
||||
buf = append(buf, varBuf[:l]...)
|
||||
} else if ac.Type == bchain.ERC721 {
|
||||
} else if ac.Type == bchain.NonFungibleToken {
|
||||
l = packVaruint(uint(len(ac.Ids)), varBuf)
|
||||
buf = append(buf, varBuf[:l]...)
|
||||
for i := range ac.Ids {
|
||||
@ -59,12 +60,12 @@ func packAddrContracts(acs *AddrContracts) []byte {
|
||||
buf = append(buf, varBuf[:l]...)
|
||||
}
|
||||
} else { // bchain.ERC1155
|
||||
l = packVaruint(uint(len(ac.IdValues)), varBuf)
|
||||
l = packVaruint(uint(len(ac.MultiTokenValues)), varBuf)
|
||||
buf = append(buf, varBuf[:l]...)
|
||||
for i := range ac.IdValues {
|
||||
l = packBigint(&ac.IdValues[i].Id, varBuf)
|
||||
for i := range ac.MultiTokenValues {
|
||||
l = packBigint(&ac.MultiTokenValues[i].Id, varBuf)
|
||||
buf = append(buf, varBuf[:l]...)
|
||||
l = packBigint(&ac.IdValues[i].Value, varBuf)
|
||||
l = packBigint(&ac.MultiTokenValues[i].Value, varBuf)
|
||||
buf = append(buf, varBuf[:l]...)
|
||||
}
|
||||
}
|
||||
@ -87,21 +88,21 @@ func unpackAddrContracts(buf []byte, addrDesc bchain.AddressDescriptor) (*AddrCo
|
||||
contract := append(bchain.AddressDescriptor(nil), buf[:eth.EthereumTypeAddressDescriptorLen]...)
|
||||
txs, l := unpackVaruint(buf[eth.EthereumTypeAddressDescriptorLen:])
|
||||
buf = buf[eth.EthereumTypeAddressDescriptorLen+l:]
|
||||
ttt := bchain.TokenTransferType(txs & 3)
|
||||
ttt := bchain.TokenType(txs & 3)
|
||||
txs >>= 2
|
||||
ac := AddrContract{
|
||||
Type: ttt,
|
||||
Contract: contract,
|
||||
Txs: txs,
|
||||
}
|
||||
if ttt == bchain.ERC20 {
|
||||
if ttt == bchain.FungibleToken {
|
||||
b, ll := unpackBigint(buf)
|
||||
buf = buf[ll:]
|
||||
ac.Value = b
|
||||
} else {
|
||||
len, ll := unpackVaruint(buf)
|
||||
buf = buf[ll:]
|
||||
if ttt == bchain.ERC721 {
|
||||
if ttt == bchain.NonFungibleToken {
|
||||
ac.Ids = make([]big.Int, len)
|
||||
for i := uint(0); i < len; i++ {
|
||||
b, ll := unpackBigint(buf)
|
||||
@ -109,14 +110,14 @@ func unpackAddrContracts(buf []byte, addrDesc bchain.AddressDescriptor) (*AddrCo
|
||||
ac.Ids[i] = b
|
||||
}
|
||||
} else {
|
||||
ac.IdValues = make([]bchain.TokenTransferIdValue, len)
|
||||
ac.MultiTokenValues = make([]bchain.MultiTokenValue, len)
|
||||
for i := uint(0); i < len; i++ {
|
||||
b, ll := unpackBigint(buf)
|
||||
buf = buf[ll:]
|
||||
ac.IdValues[i].Id = b
|
||||
ac.MultiTokenValues[i].Id = b
|
||||
b, ll = unpackBigint(buf)
|
||||
buf = buf[ll:]
|
||||
ac.IdValues[i].Value = b
|
||||
ac.MultiTokenValues[i].Value = b
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -226,9 +227,9 @@ func addToContract(c *AddrContract, contractIndex int, index int32, contract bch
|
||||
s.Add(s, v)
|
||||
}
|
||||
}
|
||||
if transfer.Type == bchain.ERC20 {
|
||||
if transfer.Type == bchain.FungibleToken {
|
||||
aggregate(&c.Value, &transfer.Value)
|
||||
} else if transfer.Type == bchain.ERC721 {
|
||||
} else if transfer.Type == bchain.NonFungibleToken {
|
||||
if index < 0 {
|
||||
// remove token from the list
|
||||
for i := range c.Ids {
|
||||
@ -242,14 +243,14 @@ func addToContract(c *AddrContract, contractIndex int, index int32, contract bch
|
||||
c.Ids = append(c.Ids, transfer.Value)
|
||||
}
|
||||
} else { // bchain.ERC1155
|
||||
for _, t := range transfer.IdValues {
|
||||
for i := range c.IdValues {
|
||||
for _, t := range transfer.MultiTokenValues {
|
||||
for i := range c.MultiTokenValues {
|
||||
// find the token in the list
|
||||
if c.IdValues[i].Id.Cmp(&t.Id) == 0 {
|
||||
aggregate(&c.IdValues[i].Value, &t.Value)
|
||||
if c.MultiTokenValues[i].Id.Cmp(&t.Id) == 0 {
|
||||
aggregate(&c.MultiTokenValues[i].Value, &t.Value)
|
||||
// if transfer from, remove if the value is zero
|
||||
if index < 0 && len(c.IdValues[i].Value.Bits()) == 0 {
|
||||
c.IdValues = append(c.IdValues[:i], c.IdValues[i+1:]...)
|
||||
if index < 0 && len(c.MultiTokenValues[i].Value.Bits()) == 0 {
|
||||
c.MultiTokenValues = append(c.MultiTokenValues[:i], c.MultiTokenValues[i+1:]...)
|
||||
}
|
||||
goto nextTransfer
|
||||
}
|
||||
@ -257,7 +258,7 @@ func addToContract(c *AddrContract, contractIndex int, index int32, contract bch
|
||||
// if not found and transfer to, add to the list
|
||||
// it is necessary to add a copy of the value so that subsequent calls to addToContract do not change the transfer value
|
||||
if index >= 0 {
|
||||
c.IdValues = append(c.IdValues, bchain.TokenTransferIdValue{
|
||||
c.MultiTokenValues = append(c.MultiTokenValues, bchain.MultiTokenValue{
|
||||
Id: t.Id,
|
||||
Value: *new(big.Int).Set(&t.Value),
|
||||
})
|
||||
@ -327,9 +328,9 @@ func (d *RocksDB) addToAddressesAndContractsEthereumType(addrDesc bchain.Address
|
||||
|
||||
type ethBlockTxContract struct {
|
||||
from, to, contract bchain.AddressDescriptor
|
||||
transferType bchain.TokenTransferType
|
||||
transferType bchain.TokenType
|
||||
value big.Int
|
||||
idValues []bchain.TokenTransferIdValue
|
||||
idValues []bchain.MultiTokenValue
|
||||
}
|
||||
|
||||
type ethInternalTransfer struct {
|
||||
@ -476,7 +477,7 @@ func (d *RocksDB) processContractTransfers(blockTx *ethBlockTx, tx *bchain.Tx, a
|
||||
bc.to = to
|
||||
bc.contract = contract
|
||||
bc.value = t.Value
|
||||
bc.idValues = t.IdValues
|
||||
bc.idValues = t.MultiTokenValues
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -699,6 +700,113 @@ func (d *RocksDB) storeInternalDataEthereumType(wb *gorocksdb.WriteBatch, blockT
|
||||
return nil
|
||||
}
|
||||
|
||||
var cachedContracts = make(map[string]*bchain.ContractInfo)
|
||||
var cachedContractsMux sync.Mutex
|
||||
|
||||
func packContractInfo(contractInfo *bchain.ContractInfo) []byte {
|
||||
buf := packString(contractInfo.Name)
|
||||
buf = append(buf, packString(contractInfo.Symbol)...)
|
||||
buf = append(buf, packString(string(contractInfo.Type))...)
|
||||
varBuf := make([]byte, vlq.MaxLen64)
|
||||
l := packVaruint(uint(contractInfo.Decimals), varBuf)
|
||||
buf = append(buf, varBuf[:l]...)
|
||||
l = packVaruint(uint(contractInfo.CreatedInBlock), varBuf)
|
||||
buf = append(buf, varBuf[:l]...)
|
||||
l = packVaruint(uint(contractInfo.DestructedInBlock), varBuf)
|
||||
buf = append(buf, varBuf[:l]...)
|
||||
return buf
|
||||
}
|
||||
|
||||
func unpackContractInfo(buf []byte) (*bchain.ContractInfo, error) {
|
||||
var contractInfo bchain.ContractInfo
|
||||
var s string
|
||||
var l int
|
||||
var ui uint
|
||||
contractInfo.Name, l = unpackString(buf)
|
||||
buf = buf[l:]
|
||||
contractInfo.Symbol, l = unpackString(buf)
|
||||
buf = buf[l:]
|
||||
s, l = unpackString(buf)
|
||||
contractInfo.Type = bchain.TokenTypeName(s)
|
||||
buf = buf[l:]
|
||||
ui, l = unpackVaruint(buf)
|
||||
contractInfo.Decimals = int(ui)
|
||||
buf = buf[l:]
|
||||
ui, l = unpackVaruint(buf)
|
||||
contractInfo.CreatedInBlock = uint32(ui)
|
||||
buf = buf[l:]
|
||||
ui, l = unpackVaruint(buf)
|
||||
contractInfo.DestructedInBlock = uint32(ui)
|
||||
return &contractInfo, nil
|
||||
}
|
||||
|
||||
func (d *RocksDB) GetContractInfoForAddress(address string) (*bchain.ContractInfo, error) {
|
||||
contract, err := d.chainParser.GetAddrDescFromAddress(address)
|
||||
if err != nil || contract == nil {
|
||||
return nil, err
|
||||
}
|
||||
return d.GetContractInfo(contract, "")
|
||||
}
|
||||
|
||||
// GetContractInfo gets contract from cache or DB and possibly updates the type from typeFromContext
|
||||
// this is because it is hard to guess the type of the contract using API, it is easier to set it the first time its usage is detected in tx
|
||||
func (d *RocksDB) GetContractInfo(contract bchain.AddressDescriptor, typeFromContext bchain.TokenTypeName) (*bchain.ContractInfo, error) {
|
||||
cacheKey := string(contract)
|
||||
cachedContractsMux.Lock()
|
||||
contractInfo, found := cachedContracts[cacheKey]
|
||||
cachedContractsMux.Unlock()
|
||||
if !found {
|
||||
val, err := d.db.GetCF(d.ro, d.cfh[cfContracts], contract)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer val.Free()
|
||||
buf := val.Data()
|
||||
if len(buf) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
contractInfo, err = unpackContractInfo(buf)
|
||||
addresses, _, _ := d.chainParser.GetAddressesFromAddrDesc(contract)
|
||||
if len(addresses) > 0 {
|
||||
contractInfo.Contract = addresses[0]
|
||||
}
|
||||
// if the type is specified and stored contractInfo has unknown type, set and store it
|
||||
if typeFromContext != bchain.UnknownTokenType && contractInfo.Type == bchain.UnknownTokenType {
|
||||
contractInfo.Type = typeFromContext
|
||||
err = d.db.PutCF(d.wo, d.cfh[cfContracts], contract, packContractInfo(contractInfo))
|
||||
}
|
||||
cachedContractsMux.Lock()
|
||||
cachedContracts[cacheKey] = contractInfo
|
||||
cachedContractsMux.Unlock()
|
||||
}
|
||||
return contractInfo, nil
|
||||
}
|
||||
|
||||
// StoreContractInfo stores contractInfo in DB
|
||||
// if CreatedInBlock==0 and DestructedInBlock!=0, it is evaluated as a desctruction of a contract, the contract info is updated
|
||||
// in all other cases the contractInfo overwrites previously stored data in DB (however it should not really happen as contract is created only once)
|
||||
func (d *RocksDB) StoreContractInfo(wb *gorocksdb.WriteBatch, contractInfo *bchain.ContractInfo) error {
|
||||
if contractInfo.Contract != "" {
|
||||
key, err := d.chainParser.GetAddrDescFromAddress(contractInfo.Contract)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if contractInfo.CreatedInBlock == 0 && contractInfo.DestructedInBlock != 0 {
|
||||
storedCI, err := d.GetContractInfo(key, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if storedCI == nil {
|
||||
return nil
|
||||
}
|
||||
storedCI.DestructedInBlock = contractInfo.DestructedInBlock
|
||||
contractInfo = storedCI
|
||||
}
|
||||
wb.PutCF(d.cfh[cfContracts], key, packContractInfo(contractInfo))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func packBlockTx(buf []byte, blockTx *ethBlockTx) []byte {
|
||||
varBuf := make([]byte, maxPackedBigintBytes)
|
||||
buf = append(buf, blockTx.btxID...)
|
||||
@ -715,7 +823,7 @@ func packBlockTx(buf []byte, blockTx *ethBlockTx) []byte {
|
||||
buf = appendAddress(buf, c.contract)
|
||||
l = packVaruint(uint(c.transferType), varBuf)
|
||||
buf = append(buf, varBuf[:l]...)
|
||||
if c.transferType == bchain.ERC1155 {
|
||||
if c.transferType == bchain.MultiToken {
|
||||
l = packVaruint(uint(len(c.idValues)), varBuf)
|
||||
buf = append(buf, varBuf[:l]...)
|
||||
for i := range c.idValues {
|
||||
@ -773,6 +881,11 @@ func (d *RocksDB) storeBlockSpecificDataEthereumType(wb *gorocksdb.WriteBatch, b
|
||||
return err
|
||||
}
|
||||
}
|
||||
for i := range blockSpecificData.Contracts {
|
||||
if err := d.StoreContractInfo(wb, &blockSpecificData.Contracts[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -823,12 +936,12 @@ func unpackBlockTx(buf []byte, pos int) (*ethBlockTx, int, error) {
|
||||
return nil, 0, err
|
||||
}
|
||||
cc, l = unpackVaruint(buf[pos:])
|
||||
c.transferType = bchain.TokenTransferType(cc)
|
||||
c.transferType = bchain.TokenType(cc)
|
||||
pos += l
|
||||
if c.transferType == bchain.ERC1155 {
|
||||
if c.transferType == bchain.MultiToken {
|
||||
cc, l = unpackVaruint(buf[pos:])
|
||||
pos += l
|
||||
c.idValues = make([]bchain.TokenTransferIdValue, cc)
|
||||
c.idValues = make([]bchain.MultiTokenValue, cc)
|
||||
for i := range c.idValues {
|
||||
c.idValues[i].Id, l = unpackBigint(buf[pos:])
|
||||
pos += l
|
||||
@ -938,9 +1051,9 @@ func (d *RocksDB) disconnectAddress(btxID []byte, internal bool, addrDesc bchain
|
||||
index = transferTo
|
||||
}
|
||||
addToContract(addrContract, contractIndex, index, btxContract.contract, &bchain.TokenTransfer{
|
||||
Type: btxContract.transferType,
|
||||
Value: btxContract.value,
|
||||
IdValues: btxContract.idValues,
|
||||
Type: btxContract.transferType,
|
||||
Value: btxContract.value,
|
||||
MultiTokenValues: btxContract.idValues,
|
||||
}, false)
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -58,11 +58,11 @@ func verifyAfterEthereumTypeBlock1(t *testing.T, d *RocksDB, afterDisconnect boo
|
||||
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser), "020102", nil},
|
||||
{
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser),
|
||||
"020100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC20)) + bigintFromStringToHex("10000000000000000000000"), nil,
|
||||
"020100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(1<<2+uint(bchain.FungibleToken)) + bigintFromStringToHex("10000000000000000000000"), nil,
|
||||
},
|
||||
{
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser),
|
||||
"010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC20)) + bigintToHex(big.NewInt(0)), nil,
|
||||
"010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(1<<2+uint(bchain.FungibleToken)) + bigintToHex(big.NewInt(0)), nil,
|
||||
},
|
||||
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser), "010002", nil},
|
||||
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser), "010101", nil},
|
||||
@ -72,6 +72,25 @@ func verifyAfterEthereumTypeBlock1(t *testing.T, d *RocksDB, afterDisconnect boo
|
||||
}
|
||||
}
|
||||
|
||||
var destructedInBlock uint
|
||||
if afterDisconnect {
|
||||
destructedInBlock = 44445
|
||||
}
|
||||
if err := checkColumn(d, cfContracts, []keyPair{
|
||||
{
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser),
|
||||
"0b436f6e7472616374203734" + // Contract 74
|
||||
"03533734" + // S74
|
||||
"054552433230" + // ERC20
|
||||
varuintToHex(12) + varuintToHex(44444) + varuintToHex(destructedInBlock),
|
||||
nil,
|
||||
},
|
||||
}); err != nil {
|
||||
{
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := checkColumn(d, cfInternalData, []keyPair{
|
||||
{
|
||||
dbtestdata.EthTxidB1T2,
|
||||
@ -98,7 +117,7 @@ func verifyAfterEthereumTypeBlock1(t *testing.T, d *RocksDB, afterDisconnect boo
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + "00" +
|
||||
dbtestdata.EthTxidB1T2 +
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) +
|
||||
"01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(uint(bchain.ERC20)) + bigintFromStringToHex("10000000000000000000000"),
|
||||
"01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(uint(bchain.FungibleToken)) + bigintFromStringToHex("10000000000000000000000"),
|
||||
nil,
|
||||
},
|
||||
}
|
||||
@ -158,43 +177,43 @@ func verifyAfterEthereumTypeBlock2(t *testing.T, d *RocksDB, wantBlockInternalDa
|
||||
if err := checkColumn(d, cfAddressContracts, []keyPair{
|
||||
{
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser),
|
||||
"010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC20)) + bigintToHex(big.NewInt(0)), nil,
|
||||
"010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(1<<2+uint(bchain.FungibleToken)) + bigintToHex(big.NewInt(0)), nil,
|
||||
},
|
||||
{
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser),
|
||||
"030202" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC1155)) + varuintToHex(1) + bigintFromStringToHex("150") + bigintFromStringToHex("1"), nil,
|
||||
"030202" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + varuintToHex(1<<2+uint(bchain.MultiToken)) + varuintToHex(1) + bigintFromStringToHex("150") + bigintFromStringToHex("1"), nil,
|
||||
},
|
||||
{
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser),
|
||||
"010101" +
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + varuintToHex(2<<2+uint(bchain.ERC20)) + bigintFromStringToHex("8086") +
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(2<<2+uint(bchain.ERC20)) + bigintFromStringToHex("871180000950184"), nil,
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + varuintToHex(2<<2+uint(bchain.FungibleToken)) + bigintFromStringToHex("8086") +
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(2<<2+uint(bchain.FungibleToken)) + bigintFromStringToHex("871180000950184"), nil,
|
||||
},
|
||||
{
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser),
|
||||
"050300" +
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(2<<2+uint(bchain.ERC20)) + bigintFromStringToHex("10000000854307892726464") +
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC20)) + bigintFromStringToHex("0") +
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC20)) + bigintFromStringToHex("0"), nil,
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(2<<2+uint(bchain.FungibleToken)) + bigintFromStringToHex("10000000854307892726464") +
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + varuintToHex(1<<2+uint(bchain.FungibleToken)) + bigintFromStringToHex("0") +
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + varuintToHex(1<<2+uint(bchain.FungibleToken)) + bigintFromStringToHex("0"), nil,
|
||||
},
|
||||
{
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr5d, d.chainParser),
|
||||
"010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC1155)) + varuintToHex(2) + bigintFromStringToHex("1776") + bigintFromStringToHex("1") + bigintFromStringToHex("1898") + bigintFromStringToHex("10"), nil,
|
||||
"010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + varuintToHex(1<<2+uint(bchain.MultiToken)) + varuintToHex(2) + bigintFromStringToHex("1776") + bigintFromStringToHex("1") + bigintFromStringToHex("1898") + bigintFromStringToHex("10"), nil,
|
||||
},
|
||||
{
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr7b, d.chainParser),
|
||||
"020000" +
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC20)) + bigintFromStringToHex("0") +
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC20)) + bigintFromStringToHex("7674999999999991915") +
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContractCd, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC721)) + varuintToHex(1) + bigintFromStringToHex("1"), nil,
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(1<<2+uint(bchain.FungibleToken)) + bigintFromStringToHex("0") +
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + varuintToHex(1<<2+uint(bchain.FungibleToken)) + bigintFromStringToHex("7674999999999991915") +
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContractCd, d.chainParser) + varuintToHex(1<<2+uint(bchain.NonFungibleToken)) + varuintToHex(1) + bigintFromStringToHex("1"), nil,
|
||||
},
|
||||
{
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr83, d.chainParser),
|
||||
"010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContractCd, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC721)) + varuintToHex(0), nil,
|
||||
"010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContractCd, d.chainParser) + varuintToHex(1<<2+uint(bchain.NonFungibleToken)) + varuintToHex(0), nil,
|
||||
},
|
||||
{
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrA3, d.chainParser),
|
||||
"010000" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + varuintToHex(1<<2+uint(bchain.ERC1155)) + varuintToHex(0), nil,
|
||||
"010000" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + varuintToHex(1<<2+uint(bchain.MultiToken)) + varuintToHex(0), nil,
|
||||
},
|
||||
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr92, d.chainParser), "010100", nil},
|
||||
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser), "030104", nil},
|
||||
@ -209,6 +228,21 @@ func verifyAfterEthereumTypeBlock2(t *testing.T, d *RocksDB, wantBlockInternalDa
|
||||
}
|
||||
}
|
||||
|
||||
if err := checkColumn(d, cfContracts, []keyPair{
|
||||
{
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser),
|
||||
"0b436f6e7472616374203734" + // Contract 74
|
||||
"03533734" + // S74
|
||||
"054552433230" + // ERC20
|
||||
varuintToHex(12) + varuintToHex(44444) + varuintToHex(44445),
|
||||
nil,
|
||||
},
|
||||
}); err != nil {
|
||||
{
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := checkColumn(d, cfInternalData, []keyPair{
|
||||
{
|
||||
dbtestdata.EthTxidB1T2,
|
||||
@ -243,22 +277,22 @@ func verifyAfterEthereumTypeBlock2(t *testing.T, d *RocksDB, wantBlockInternalDa
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser) + "00" +
|
||||
dbtestdata.EthTxidB2T2 +
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract47, d.chainParser) +
|
||||
"04" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + varuintToHex(uint(bchain.ERC20)) + bigintFromStringToHex("7675000000000000001") +
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(uint(bchain.ERC20)) + bigintFromStringToHex("854307892726464") +
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr7b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(uint(bchain.ERC20)) + bigintFromStringToHex("871180000950184") +
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr7b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + varuintToHex(uint(bchain.ERC20)) + bigintFromStringToHex("7674999999999991915") +
|
||||
"04" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + varuintToHex(uint(bchain.FungibleToken)) + bigintFromStringToHex("7675000000000000001") +
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(uint(bchain.FungibleToken)) + bigintFromStringToHex("854307892726464") +
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr7b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + varuintToHex(uint(bchain.FungibleToken)) + bigintFromStringToHex("871180000950184") +
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr7b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + varuintToHex(uint(bchain.FungibleToken)) + bigintFromStringToHex("7674999999999991915") +
|
||||
dbtestdata.EthTxidB2T3 +
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr83, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContractCd, d.chainParser) +
|
||||
"01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr83, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr7b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContractCd, d.chainParser) + varuintToHex(uint(bchain.ERC721)) + bigintFromStringToHex("1") +
|
||||
"01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr83, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr7b, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContractCd, d.chainParser) + varuintToHex(uint(bchain.NonFungibleToken)) + bigintFromStringToHex("1") +
|
||||
dbtestdata.EthTxidB2T4 +
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr92, d.chainParser) +
|
||||
"01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrA3, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + varuintToHex(uint(bchain.ERC1155)) + "01" + bigintFromStringToHex("150") + bigintFromStringToHex("1") +
|
||||
"01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrA3, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + varuintToHex(uint(bchain.MultiToken)) + "01" + bigintFromStringToHex("150") + bigintFromStringToHex("1") +
|
||||
dbtestdata.EthTxidB2T5 +
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr5d, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) +
|
||||
"01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrZero, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr5d, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + varuintToHex(uint(bchain.ERC1155)) + "02" + bigintFromStringToHex("1776") + bigintFromStringToHex("1") + bigintFromStringToHex("1898") + bigintFromStringToHex("10") +
|
||||
"01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrZero, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr5d, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract6f, d.chainParser) + varuintToHex(uint(bchain.MultiToken)) + "02" + bigintFromStringToHex("1776") + bigintFromStringToHex("1") + bigintFromStringToHex("1898") + bigintFromStringToHex("10") +
|
||||
dbtestdata.EthTxidB2T6 +
|
||||
dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) +
|
||||
"01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + varuintToHex(uint(bchain.ERC20)) + bigintFromStringToHex("10000000000000000000000"),
|
||||
"01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser) + varuintToHex(uint(bchain.FungibleToken)) + bigintFromStringToHex("10000000000000000000000"),
|
||||
nil,
|
||||
},
|
||||
}); err != nil {
|
||||
@ -722,13 +756,13 @@ func Test_packUnpackAddrContracts(t *testing.T) {
|
||||
InternalTxs: 8873,
|
||||
Contracts: []AddrContract{
|
||||
{
|
||||
Type: bchain.ERC20,
|
||||
Type: bchain.FungibleToken,
|
||||
Contract: addressToAddrDesc(dbtestdata.EthAddrContract0d, parser),
|
||||
Txs: 8,
|
||||
Value: *big.NewInt(793201132),
|
||||
},
|
||||
{
|
||||
Type: bchain.ERC721,
|
||||
Type: bchain.NonFungibleToken,
|
||||
Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser),
|
||||
Txs: 41235,
|
||||
Ids: []big.Int{
|
||||
@ -740,10 +774,10 @@ func Test_packUnpackAddrContracts(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: bchain.ERC1155,
|
||||
Type: bchain.MultiToken,
|
||||
Contract: addressToAddrDesc(dbtestdata.EthAddrContract4a, parser),
|
||||
Txs: 64,
|
||||
IdValues: []bchain.TokenTransferIdValue{
|
||||
MultiTokenValues: []bchain.MultiTokenValue{
|
||||
{
|
||||
Id: *big.NewInt(1),
|
||||
Value: *big.NewInt(1412341234),
|
||||
@ -796,7 +830,7 @@ func Test_addToContracts(t *testing.T) {
|
||||
index: 1,
|
||||
contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser),
|
||||
transfer: &bchain.TokenTransfer{
|
||||
Type: bchain.ERC20,
|
||||
Type: bchain.FungibleToken,
|
||||
Value: *big.NewInt(123456),
|
||||
},
|
||||
addTxCount: true,
|
||||
@ -805,7 +839,7 @@ func Test_addToContracts(t *testing.T) {
|
||||
wantAddrContracts: &AddrContracts{
|
||||
Contracts: []AddrContract{
|
||||
{
|
||||
Type: bchain.ERC20,
|
||||
Type: bchain.FungibleToken,
|
||||
Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser),
|
||||
Txs: 1,
|
||||
Value: *big.NewInt(123456),
|
||||
@ -819,7 +853,7 @@ func Test_addToContracts(t *testing.T) {
|
||||
index: ^1,
|
||||
contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser),
|
||||
transfer: &bchain.TokenTransfer{
|
||||
Type: bchain.ERC20,
|
||||
Type: bchain.FungibleToken,
|
||||
Value: *big.NewInt(23456),
|
||||
},
|
||||
addTxCount: true,
|
||||
@ -828,7 +862,7 @@ func Test_addToContracts(t *testing.T) {
|
||||
wantAddrContracts: &AddrContracts{
|
||||
Contracts: []AddrContract{
|
||||
{
|
||||
Type: bchain.ERC20,
|
||||
Type: bchain.FungibleToken,
|
||||
Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser),
|
||||
Value: *big.NewInt(100000),
|
||||
Txs: 2,
|
||||
@ -842,7 +876,7 @@ func Test_addToContracts(t *testing.T) {
|
||||
index: 1,
|
||||
contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser),
|
||||
transfer: &bchain.TokenTransfer{
|
||||
Type: bchain.ERC721,
|
||||
Type: bchain.NonFungibleToken,
|
||||
Value: *big.NewInt(1),
|
||||
},
|
||||
addTxCount: true,
|
||||
@ -851,13 +885,13 @@ func Test_addToContracts(t *testing.T) {
|
||||
wantAddrContracts: &AddrContracts{
|
||||
Contracts: []AddrContract{
|
||||
{
|
||||
Type: bchain.ERC20,
|
||||
Type: bchain.FungibleToken,
|
||||
Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser),
|
||||
Value: *big.NewInt(100000),
|
||||
Txs: 2,
|
||||
},
|
||||
{
|
||||
Type: bchain.ERC721,
|
||||
Type: bchain.NonFungibleToken,
|
||||
Contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser),
|
||||
Txs: 1,
|
||||
Ids: []big.Int{*big.NewInt(1)},
|
||||
@ -871,7 +905,7 @@ func Test_addToContracts(t *testing.T) {
|
||||
index: 1,
|
||||
contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser),
|
||||
transfer: &bchain.TokenTransfer{
|
||||
Type: bchain.ERC721,
|
||||
Type: bchain.NonFungibleToken,
|
||||
Value: *big.NewInt(2),
|
||||
},
|
||||
addTxCount: true,
|
||||
@ -880,13 +914,13 @@ func Test_addToContracts(t *testing.T) {
|
||||
wantAddrContracts: &AddrContracts{
|
||||
Contracts: []AddrContract{
|
||||
{
|
||||
Type: bchain.ERC20,
|
||||
Type: bchain.FungibleToken,
|
||||
Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser),
|
||||
Value: *big.NewInt(100000),
|
||||
Txs: 2,
|
||||
},
|
||||
{
|
||||
Type: bchain.ERC721,
|
||||
Type: bchain.NonFungibleToken,
|
||||
Contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser),
|
||||
Txs: 2,
|
||||
Ids: []big.Int{*big.NewInt(1), *big.NewInt(2)},
|
||||
@ -900,7 +934,7 @@ func Test_addToContracts(t *testing.T) {
|
||||
index: ^1,
|
||||
contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser),
|
||||
transfer: &bchain.TokenTransfer{
|
||||
Type: bchain.ERC721,
|
||||
Type: bchain.NonFungibleToken,
|
||||
Value: *big.NewInt(1),
|
||||
},
|
||||
addTxCount: false,
|
||||
@ -909,13 +943,13 @@ func Test_addToContracts(t *testing.T) {
|
||||
wantAddrContracts: &AddrContracts{
|
||||
Contracts: []AddrContract{
|
||||
{
|
||||
Type: bchain.ERC20,
|
||||
Type: bchain.FungibleToken,
|
||||
Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser),
|
||||
Value: *big.NewInt(100000),
|
||||
Txs: 2,
|
||||
},
|
||||
{
|
||||
Type: bchain.ERC721,
|
||||
Type: bchain.NonFungibleToken,
|
||||
Contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser),
|
||||
Txs: 2,
|
||||
Ids: []big.Int{*big.NewInt(2)},
|
||||
@ -929,8 +963,8 @@ func Test_addToContracts(t *testing.T) {
|
||||
index: 1,
|
||||
contract: addressToAddrDesc(dbtestdata.EthAddrContractCd, parser),
|
||||
transfer: &bchain.TokenTransfer{
|
||||
Type: bchain.ERC1155,
|
||||
IdValues: []bchain.TokenTransferIdValue{
|
||||
Type: bchain.MultiToken,
|
||||
MultiTokenValues: []bchain.MultiTokenValue{
|
||||
{
|
||||
Id: *big.NewInt(11),
|
||||
Value: *big.NewInt(56789),
|
||||
@ -943,22 +977,22 @@ func Test_addToContracts(t *testing.T) {
|
||||
wantAddrContracts: &AddrContracts{
|
||||
Contracts: []AddrContract{
|
||||
{
|
||||
Type: bchain.ERC20,
|
||||
Type: bchain.FungibleToken,
|
||||
Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser),
|
||||
Value: *big.NewInt(100000),
|
||||
Txs: 2,
|
||||
},
|
||||
{
|
||||
Type: bchain.ERC721,
|
||||
Type: bchain.NonFungibleToken,
|
||||
Contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser),
|
||||
Txs: 2,
|
||||
Ids: []big.Int{*big.NewInt(2)},
|
||||
},
|
||||
{
|
||||
Type: bchain.ERC1155,
|
||||
Type: bchain.MultiToken,
|
||||
Contract: addressToAddrDesc(dbtestdata.EthAddrContractCd, parser),
|
||||
Txs: 1,
|
||||
IdValues: []bchain.TokenTransferIdValue{
|
||||
MultiTokenValues: []bchain.MultiTokenValue{
|
||||
{
|
||||
Id: *big.NewInt(11),
|
||||
Value: *big.NewInt(56789),
|
||||
@ -974,8 +1008,8 @@ func Test_addToContracts(t *testing.T) {
|
||||
index: 1,
|
||||
contract: addressToAddrDesc(dbtestdata.EthAddrContractCd, parser),
|
||||
transfer: &bchain.TokenTransfer{
|
||||
Type: bchain.ERC1155,
|
||||
IdValues: []bchain.TokenTransferIdValue{
|
||||
Type: bchain.MultiToken,
|
||||
MultiTokenValues: []bchain.MultiTokenValue{
|
||||
{
|
||||
Id: *big.NewInt(11),
|
||||
Value: *big.NewInt(111),
|
||||
@ -992,22 +1026,22 @@ func Test_addToContracts(t *testing.T) {
|
||||
wantAddrContracts: &AddrContracts{
|
||||
Contracts: []AddrContract{
|
||||
{
|
||||
Type: bchain.ERC20,
|
||||
Type: bchain.FungibleToken,
|
||||
Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser),
|
||||
Value: *big.NewInt(100000),
|
||||
Txs: 2,
|
||||
},
|
||||
{
|
||||
Type: bchain.ERC721,
|
||||
Type: bchain.NonFungibleToken,
|
||||
Contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser),
|
||||
Txs: 2,
|
||||
Ids: []big.Int{*big.NewInt(2)},
|
||||
},
|
||||
{
|
||||
Type: bchain.ERC1155,
|
||||
Type: bchain.MultiToken,
|
||||
Contract: addressToAddrDesc(dbtestdata.EthAddrContractCd, parser),
|
||||
Txs: 2,
|
||||
IdValues: []bchain.TokenTransferIdValue{
|
||||
MultiTokenValues: []bchain.MultiTokenValue{
|
||||
{
|
||||
Id: *big.NewInt(11),
|
||||
Value: *big.NewInt(56900),
|
||||
@ -1027,8 +1061,8 @@ func Test_addToContracts(t *testing.T) {
|
||||
index: ^1,
|
||||
contract: addressToAddrDesc(dbtestdata.EthAddrContractCd, parser),
|
||||
transfer: &bchain.TokenTransfer{
|
||||
Type: bchain.ERC1155,
|
||||
IdValues: []bchain.TokenTransferIdValue{
|
||||
Type: bchain.MultiToken,
|
||||
MultiTokenValues: []bchain.MultiTokenValue{
|
||||
{
|
||||
Id: *big.NewInt(11),
|
||||
Value: *big.NewInt(112),
|
||||
@ -1045,22 +1079,22 @@ func Test_addToContracts(t *testing.T) {
|
||||
wantAddrContracts: &AddrContracts{
|
||||
Contracts: []AddrContract{
|
||||
{
|
||||
Type: bchain.ERC20,
|
||||
Type: bchain.FungibleToken,
|
||||
Contract: addressToAddrDesc(dbtestdata.EthAddrContract47, parser),
|
||||
Value: *big.NewInt(100000),
|
||||
Txs: 2,
|
||||
},
|
||||
{
|
||||
Type: bchain.ERC721,
|
||||
Type: bchain.NonFungibleToken,
|
||||
Contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser),
|
||||
Txs: 2,
|
||||
Ids: []big.Int{*big.NewInt(2)},
|
||||
},
|
||||
{
|
||||
Type: bchain.ERC1155,
|
||||
Type: bchain.MultiToken,
|
||||
Contract: addressToAddrDesc(dbtestdata.EthAddrContractCd, parser),
|
||||
Txs: 3,
|
||||
IdValues: []bchain.TokenTransferIdValue{
|
||||
MultiTokenValues: []bchain.MultiTokenValue{
|
||||
{
|
||||
Id: *big.NewInt(11),
|
||||
Value: *big.NewInt(56788),
|
||||
@ -1119,7 +1153,7 @@ func Test_packUnpackBlockTx(t *testing.T) {
|
||||
from: addressToAddrDesc(dbtestdata.EthAddr20, parser),
|
||||
to: addressToAddrDesc(dbtestdata.EthAddr5d, parser),
|
||||
contract: addressToAddrDesc(dbtestdata.EthAddrContract4a, parser),
|
||||
transferType: bchain.ERC20,
|
||||
transferType: bchain.FungibleToken,
|
||||
value: *big.NewInt(10000),
|
||||
},
|
||||
},
|
||||
@ -1137,22 +1171,22 @@ func Test_packUnpackBlockTx(t *testing.T) {
|
||||
from: addressToAddrDesc(dbtestdata.EthAddr20, parser),
|
||||
to: addressToAddrDesc(dbtestdata.EthAddr3e, parser),
|
||||
contract: addressToAddrDesc(dbtestdata.EthAddrContract4a, parser),
|
||||
transferType: bchain.ERC20,
|
||||
transferType: bchain.FungibleToken,
|
||||
value: *big.NewInt(987654321),
|
||||
},
|
||||
{
|
||||
from: addressToAddrDesc(dbtestdata.EthAddr4b, parser),
|
||||
to: addressToAddrDesc(dbtestdata.EthAddr55, parser),
|
||||
contract: addressToAddrDesc(dbtestdata.EthAddrContract6f, parser),
|
||||
transferType: bchain.ERC721,
|
||||
transferType: bchain.NonFungibleToken,
|
||||
value: *big.NewInt(13),
|
||||
},
|
||||
{
|
||||
from: addressToAddrDesc(dbtestdata.EthAddr5d, parser),
|
||||
to: addressToAddrDesc(dbtestdata.EthAddr7b, parser),
|
||||
contract: addressToAddrDesc(dbtestdata.EthAddrContractCd, parser),
|
||||
transferType: bchain.ERC1155,
|
||||
idValues: []bchain.TokenTransferIdValue{
|
||||
transferType: bchain.MultiToken,
|
||||
idValues: []bchain.MultiTokenValue{
|
||||
{
|
||||
Id: *big.NewInt(1234),
|
||||
Value: *big.NewInt(98765),
|
||||
@ -1222,3 +1256,45 @@ func Test_packUnpackFourByteSignature(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_packUnpackContractInfo(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
contractInfo bchain.ContractInfo
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
contractInfo: bchain.ContractInfo{},
|
||||
},
|
||||
{
|
||||
name: "unknown",
|
||||
contractInfo: bchain.ContractInfo{
|
||||
Type: bchain.UnknownTokenType,
|
||||
Name: "Test contract",
|
||||
Symbol: "TCT",
|
||||
Decimals: 18,
|
||||
CreatedInBlock: 1234567,
|
||||
DestructedInBlock: 234567890,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ERC20",
|
||||
contractInfo: bchain.ContractInfo{
|
||||
Type: bchain.ERC20TokenType,
|
||||
Name: "GreenContract🟢",
|
||||
Symbol: "🟢",
|
||||
Decimals: 0,
|
||||
CreatedInBlock: 1,
|
||||
DestructedInBlock: 2,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
buf := packContractInfo(&tt.contractInfo)
|
||||
if got, err := unpackContractInfo(buf); !reflect.DeepEqual(*got, tt.contractInfo) || err != nil {
|
||||
t.Errorf("packUnpackContractInfo() = %v, want %v, error %v", *got, tt.contractInfo, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -562,7 +562,7 @@ func isOwnAddress(td *TemplateData, a string) bool {
|
||||
}
|
||||
|
||||
// called from template, returns count of token transfers of given type in a tx
|
||||
func tokenTransfersCount(tx *api.Tx, t api.TokenType) int {
|
||||
func tokenTransfersCount(tx *api.Tx, t bchain.TokenTypeName) int {
|
||||
count := 0
|
||||
for i := range tx.TokenTransfers {
|
||||
if tx.TokenTransfers[i].Type == t {
|
||||
@ -573,7 +573,7 @@ func tokenTransfersCount(tx *api.Tx, t api.TokenType) int {
|
||||
}
|
||||
|
||||
// called from template, returns count of tokens in array of given type
|
||||
func tokenCount(tokens []api.Token, t api.TokenType) int {
|
||||
func tokenCount(tokens []api.Token, t bchain.TokenTypeName) int {
|
||||
count := 0
|
||||
for i := range tokens {
|
||||
if tokens[i].Type == t {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -896,7 +896,7 @@ func (s *WebsocketServer) sendOnNewTxAddr(stringAddressDescriptor string, tx *ap
|
||||
}
|
||||
|
||||
func (s *WebsocketServer) getNewTxSubscriptions(tx *bchain.MempoolTx) map[string]struct{} {
|
||||
// check if there is any subscription in inputs, outputs and erc20
|
||||
// check if there is any subscription in inputs, outputs and token transfers
|
||||
s.addressSubscriptionsLock.Lock()
|
||||
defer s.addressSubscriptionsLock.Unlock()
|
||||
subscribed := make(map[string]struct{})
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{{define "specific"}}{{$cs := .CoinShortcut}}{{$addr := .Address}}{{$data := .}}
|
||||
<h1>{{if $addr.Erc20Contract}}Contract {{$addr.Erc20Contract.Name}} ({{$addr.Erc20Contract.Symbol}}){{else}}Address{{end}} <small class="text-muted">{{formatAmount $addr.BalanceSat}} {{$cs}}</small>
|
||||
<h1>{{if $addr.ContractInfo}}Contract {{$addr.ContractInfo.Name}} ({{$addr.ContractInfo.Symbol}}){{else}}Address{{end}} <small class="text-muted">{{formatAmount $addr.BalanceSat}} {{$cs}}</small>
|
||||
</h1>
|
||||
<div class="alert alert-data ellipsis">
|
||||
<span class="data">{{$addr.AddrStr}}</span>
|
||||
@ -98,7 +98,7 @@
|
||||
<tr>
|
||||
<td class="data ellipsis">{{if $t.Contract}}<a href="/address/{{$t.Contract}}">{{$t.Name}}</a>{{else}}{{$t.Name}}{{end}}</td>
|
||||
<td class="data">
|
||||
{{range $i, $iv := $t.IdValues}}{{if $i}}, {{end}}{{formatAmountWithDecimals $iv.Id 0}}:{{formatAmountWithDecimals $iv.Value 0}} {{$t.Symbol}}{{end}}
|
||||
{{range $i, $iv := $t.MultiTokenValues}}{{if $i}}, {{end}}{{formatAmountWithDecimals $iv.Id 0}}:{{formatAmountWithDecimals $iv.Value 0}} {{$t.Symbol}}{{end}}
|
||||
</td>
|
||||
<td class="data">{{$t.Transfers}}</td>
|
||||
</tr>
|
||||
|
||||
@ -267,7 +267,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 text-right" style="padding: .4rem 0;">
|
||||
{{- range $iv := $tt.Values -}}
|
||||
{{- range $iv := $tt.MultiTokenValues -}}
|
||||
{{formatAmountWithDecimals $iv.Id 0}}:{{formatAmountWithDecimals $iv.Value 0}} {{$tt.Symbol}}
|
||||
{{- end -}}
|
||||
</div>
|
||||
|
||||
@ -128,6 +128,19 @@ var EthTx4InternalData = &bchain.EthereumInternalData{
|
||||
},
|
||||
}
|
||||
|
||||
var Block1SpecificData = &bchain.EthereumBlockSpecificData{
|
||||
Contracts: []bchain.ContractInfo{
|
||||
{
|
||||
Contract: EthAddrContract4a,
|
||||
Type: bchain.ERC20TokenType,
|
||||
Name: "Contract 74",
|
||||
Symbol: "S74",
|
||||
Decimals: 12,
|
||||
CreatedInBlock: 44444,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var Block2SpecificData = &bchain.EthereumBlockSpecificData{
|
||||
InternalDataError: "test error",
|
||||
AddressAliasRecords: []bchain.AddressAliasRecord{
|
||||
@ -140,6 +153,12 @@ var Block2SpecificData = &bchain.EthereumBlockSpecificData{
|
||||
Name: "address20",
|
||||
},
|
||||
},
|
||||
Contracts: []bchain.ContractInfo{
|
||||
{
|
||||
Contract: EthAddrContract4a,
|
||||
DestructedInBlock: 44445,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
type packedAndInternal struct {
|
||||
@ -182,6 +201,7 @@ func GetTestEthereumTypeBlock1(parser bchain.BlockChainParser) *bchain.Block {
|
||||
packed: EthTx2Packed,
|
||||
internal: EthTx2InternalData,
|
||||
}}, parser),
|
||||
CoinSpecificData: Block1SpecificData,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -117,13 +117,15 @@ func (c *fakeBlockChainEthereumType) EthereumTypeGetNonce(addrDesc bchain.Addres
|
||||
return uint64(addrDesc[0]), nil
|
||||
}
|
||||
|
||||
func (c *fakeBlockChainEthereumType) EthereumTypeGetErc20ContractInfo(contractDesc bchain.AddressDescriptor) (*bchain.Erc20Contract, error) {
|
||||
func (c *fakeBlockChainEthereumType) GetContractInfo(contractDesc bchain.AddressDescriptor) (*bchain.ContractInfo, error) {
|
||||
addresses, _, _ := c.Parser.GetAddressesFromAddrDesc(contractDesc)
|
||||
return &bchain.Erc20Contract{
|
||||
Contract: addresses[0],
|
||||
Name: "Contract " + strconv.Itoa(int(contractDesc[0])),
|
||||
Symbol: "S" + strconv.Itoa(int(contractDesc[0])),
|
||||
Decimals: 18,
|
||||
return &bchain.ContractInfo{
|
||||
Type: bchain.ERC20TokenType,
|
||||
Contract: addresses[0],
|
||||
Name: "Contract " + strconv.Itoa(int(contractDesc[0])),
|
||||
Symbol: "S" + strconv.Itoa(int(contractDesc[0])),
|
||||
Decimals: 18,
|
||||
CreatedInBlock: 12345,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user