Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Base Support #1150

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
59 changes: 32 additions & 27 deletions api/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,21 +158,23 @@ type MultiTokenValue struct {

// Token contains info about tokens held by an address
type Token struct {
Type bchain.TokenTypeName `json:"type" ts_type:"'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155'"`
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"`
BaseValue float64 `json:"baseValue,omitempty"` // value in the base currency (ETH for Ethereum)
SecondaryValue float64 `json:"secondaryValue,omitempty"` // value in secondary (fiat) currency, if specified
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:"-"`
// Deprecated: Use Standard instead.
Type bchain.TokenStandardName `json:"type" ts_type:"'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155'"`
Standard bchain.TokenStandardName `json:"standard" ts_type:"'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155'"`
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"`
BaseValue float64 `json:"baseValue,omitempty"` // value in the base currency (ETH for Ethereum)
SecondaryValue float64 `json:"secondaryValue,omitempty"` // value in secondary (fiat) currency, if specified
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:"-"`
}

// Tokens is array of Token
Expand Down Expand Up @@ -204,15 +206,17 @@ func (a Tokens) Less(i, j int) bool {

// TokenTransfer contains info about a token transfer done in a transaction
type TokenTransfer struct {
Type bchain.TokenTypeName `json:"type"`
From string `json:"from"`
To string `json:"to"`
Contract string `json:"contract"`
Name string `json:"name,omitempty"`
Symbol string `json:"symbol,omitempty"`
Decimals int `json:"decimals,omitempty"`
Value *Amount `json:"value,omitempty"`
MultiTokenValues []MultiTokenValue `json:"multiTokenValues,omitempty"`
// Deprecated: Use Standard instead.
Type bchain.TokenStandardName `json:"type"`
Standard bchain.TokenStandardName `json:"standard"`
From string `json:"from"`
To string `json:"to"`
Contract string `json:"contract"`
Name string `json:"name,omitempty"`
Symbol string `json:"symbol,omitempty"`
Decimals int `json:"decimals,omitempty"`
Value *Amount `json:"value,omitempty"`
MultiTokenValues []MultiTokenValue `json:"multiTokenValues,omitempty"`
}

type EthereumInternalTransfer struct {
Expand Down Expand Up @@ -357,9 +361,10 @@ type Address struct {
TotalBaseValue float64 `json:"totalBaseValue,omitempty"` // value including tokens in base currency
TotalSecondaryValue float64 `json:"totalSecondaryValue,omitempty"` // value including tokens in secondary currency
ContractInfo *bchain.ContractInfo `json:"contractInfo,omitempty"`
Erc20Contract *bchain.ContractInfo `json:"erc20Contract,omitempty"` // deprecated
AddressAliases AddressAliasesMap `json:"addressAliases,omitempty"`
StakingPools []StakingPool `json:"stakingPools,omitempty"`
// Deprecated: replaced by ContractInfo
Erc20Contract *bchain.ContractInfo `json:"erc20Contract,omitempty"`
AddressAliases AddressAliasesMap `json:"addressAliases,omitempty"`
StakingPools []StakingPool `json:"stakingPools,omitempty"`
// helpers for explorer
Filter string `json:"-"`
XPubAddresses map[string]struct{} `json:"-"`
Expand Down
74 changes: 41 additions & 33 deletions api/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,10 @@ func (w *Worker) getAddressAliases(addresses map[string]struct{}) AddressAliases
if err != nil || addrDesc == nil {
continue
}
ci, err := w.db.GetContractInfo(addrDesc, bchain.UnknownTokenType)
ci, err := w.db.GetContractInfo(addrDesc, bchain.UnknownTokenStandard)
if err == nil && ci != nil {
if ci.Type == bchain.UnhandledTokenType {
ci, _, err = w.getContractDescriptorInfo(addrDesc, bchain.UnknownTokenType)
if ci.Standard == bchain.UnhandledTokenStandard {
ci, _, err = w.getContractDescriptorInfo(addrDesc, bchain.UnknownTokenStandard)
}
if err == nil && ci != nil && ci.Name != "" {
aliases[a] = AddressAlias{Type: "Contract", Alias: ci.Name}
Expand Down Expand Up @@ -622,47 +622,47 @@ func (w *Worker) GetTransactionFromMempoolTx(mempoolTx *bchain.MempoolTx) (*Tx,
return r, nil
}

func (w *Worker) GetContractInfo(contract string, typeFromContext bchain.TokenTypeName) (*bchain.ContractInfo, bool, error) {
func (w *Worker) GetContractInfo(contract string, standardFromContext bchain.TokenStandardName) (*bchain.ContractInfo, bool, error) {
cd, err := w.chainParser.GetAddrDescFromAddress(contract)
if err != nil {
return nil, false, err
}
return w.getContractDescriptorInfo(cd, typeFromContext)
return w.getContractDescriptorInfo(cd, standardFromContext)
}

func (w *Worker) getContractDescriptorInfo(cd bchain.AddressDescriptor, typeFromContext bchain.TokenTypeName) (*bchain.ContractInfo, bool, error) {
func (w *Worker) getContractDescriptorInfo(cd bchain.AddressDescriptor, standardFromContext bchain.TokenStandardName) (*bchain.ContractInfo, bool, error) {
var err error
validContract := true
contractInfo, err := w.db.GetContractInfo(cd, typeFromContext)
contractInfo, err := w.db.GetContractInfo(cd, standardFromContext)
if err != nil {
return nil, false, err
}
if contractInfo == nil {
// log warning only if the contract should have been known from processing of the internal data
if eth.ProcessInternalTransactions {
glog.Warningf("Contract %v %v not found in DB", cd, typeFromContext)
glog.Warningf("Contract %v %v not found in DB", cd, standardFromContext)
}
contractInfo, err = w.chain.GetContractInfo(cd)
if err != nil {
glog.Errorf("GetContractInfo from chain error %v, contract %v", err, cd)
}
if contractInfo == nil {
contractInfo = &bchain.ContractInfo{Type: bchain.UnknownTokenType, Decimals: w.chainParser.AmountDecimals()}
contractInfo = &bchain.ContractInfo{Standard: bchain.UnknownTokenStandard, Decimals: w.chainParser.AmountDecimals()}
addresses, _, _ := w.chainParser.GetAddressesFromAddrDesc(cd)
if len(addresses) > 0 {
contractInfo.Contract = addresses[0]
}

validContract = false
} else {
if typeFromContext != bchain.UnknownTokenType && contractInfo.Type == bchain.UnknownTokenType {
contractInfo.Type = typeFromContext
if standardFromContext != bchain.UnknownTokenStandard && contractInfo.Standard == bchain.UnknownTokenStandard {
contractInfo.Standard = standardFromContext
}
if err = w.db.StoreContractInfo(contractInfo); err != nil {
glog.Errorf("StoreContractInfo error %v, contract %v", err, cd)
}
}
} else if (contractInfo.Type == bchain.UnhandledTokenType || len(contractInfo.Name) > 0 && contractInfo.Name[0] == 0) || (len(contractInfo.Symbol) > 0 && contractInfo.Symbol[0] == 0) {
} else if (contractInfo.Standard == bchain.UnhandledTokenStandard || len(contractInfo.Name) > 0 && contractInfo.Name[0] == 0) || (len(contractInfo.Symbol) > 0 && contractInfo.Symbol[0] == 0) {
// fix contract name/symbol that was parsed as a string consisting of zeroes
blockchainContractInfo, err := w.chain.GetContractInfo(cd)
if err != nil {
Expand All @@ -681,9 +681,9 @@ func (w *Worker) getContractDescriptorInfo(cd bchain.AddressDescriptor, typeFrom
if blockchainContractInfo != nil {
contractInfo.Decimals = blockchainContractInfo.Decimals
}
if contractInfo.Type == bchain.UnhandledTokenType {
glog.Infof("Contract %v %v [%s] handled", cd, typeFromContext, contractInfo.Name)
contractInfo.Type = typeFromContext
if contractInfo.Standard == bchain.UnhandledTokenStandard {
glog.Infof("Contract %v %v [%s] handled", cd, standardFromContext, contractInfo.Name)
contractInfo.Standard = standardFromContext
}
if err = w.db.StoreContractInfo(contractInfo); err != nil {
glog.Errorf("StoreContractInfo error %v, contract %v", err, cd)
Expand All @@ -700,12 +700,12 @@ func (w *Worker) getEthereumTokensTransfers(transfers bchain.TokenTransfers, add
contractCache := make(contractInfoCache)
for i := range transfers {
t := transfers[i]
typeName := bchain.EthereumTokenTypeMap[t.Type]
standard := bchain.EthereumTokenStandardMap[t.Standard]
var contractInfo *bchain.ContractInfo
if info, ok := contractCache[t.Contract]; ok {
contractInfo = info
} else {
info, _, err := w.GetContractInfo(t.Contract, typeName)
info, _, err := w.GetContractInfo(t.Contract, standard)
if err != nil {
glog.Errorf("getContractInfo error %v, contract %v", err, t.Contract)
continue
Expand All @@ -715,7 +715,7 @@ func (w *Worker) getEthereumTokensTransfers(transfers bchain.TokenTransfers, add
}
var value *Amount
var values []MultiTokenValue
if t.Type == bchain.MultiToken {
if t.Standard == bchain.MultiToken {
values = make([]MultiTokenValue, len(t.MultiTokenValues))
for j := range values {
values[j].Id = (*Amount)(&t.MultiTokenValues[j].Id)
Expand All @@ -727,7 +727,8 @@ func (w *Worker) getEthereumTokensTransfers(transfers bchain.TokenTransfers, add
aggregateAddress(addresses, t.From)
aggregateAddress(addresses, t.To)
tokens[i] = TokenTransfer{
Type: typeName,
Type: standard,
Standard: standard,
Contract: t.Contract,
From: t.From,
To: t.To,
Expand Down Expand Up @@ -755,7 +756,7 @@ func (w *Worker) GetEthereumTokenURI(contract string, id string) (string, *bchai
if err != nil {
return "", nil, err
}
ci, _, err := w.getContractDescriptorInfo(cd, bchain.UnknownTokenType)
ci, _, err := w.getContractDescriptorInfo(cd, bchain.UnknownTokenStandard)
if err != nil {
return "", nil, err
}
Expand Down Expand Up @@ -957,23 +958,24 @@ func computePaging(count, page, itemsOnPage int) (Paging, int, int, int) {
}

func (w *Worker) getEthereumContractBalance(addrDesc bchain.AddressDescriptor, index int, c *db.AddrContract, details AccountDetails, ticker *common.CurrencyRatesTicker, secondaryCoin string) (*Token, error) {
typeName := bchain.EthereumTokenTypeMap[c.Type]
ci, validContract, err := w.getContractDescriptorInfo(c.Contract, typeName)
standard := bchain.EthereumTokenStandardMap[c.Standard]
ci, validContract, err := w.getContractDescriptorInfo(c.Contract, standard)
if err != nil {
return nil, errors.Annotatef(err, "getEthereumContractBalance %v", c.Contract)
}
t := Token{
Contract: ci.Contract,
Name: ci.Name,
Symbol: ci.Symbol,
Type: typeName,
Type: standard,
Standard: standard,
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.FungibleToken {
if c.Standard == 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 {
Expand Down Expand Up @@ -1022,7 +1024,7 @@ func (w *Worker) getEthereumContractBalance(addrDesc bchain.AddressDescriptor, i
// a fallback method in case internal transactions are not processed and there is no indexed info about contract balance for an address
func (w *Worker) getEthereumContractBalanceFromBlockchain(addrDesc, contract bchain.AddressDescriptor, details AccountDetails) (*Token, error) {
var b *big.Int
ci, validContract, err := w.getContractDescriptorInfo(contract, bchain.UnknownTokenType)
ci, validContract, err := w.getContractDescriptorInfo(contract, bchain.UnknownTokenStandard)
if err != nil {
return nil, errors.Annotatef(err, "GetContractInfo %v", contract)
}
Expand All @@ -1037,7 +1039,8 @@ func (w *Worker) getEthereumContractBalanceFromBlockchain(addrDesc, contract bch
b = nil
}
return &Token{
Type: ci.Type,
Type: ci.Standard,
Standard: ci.Standard,
BalanceSat: (*Amount)(b),
Contract: ci.Contract,
Name: ci.Name,
Expand Down Expand Up @@ -1142,12 +1145,12 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
d.tokens = d.tokens[:j]
sort.Sort(d.tokens)
}
d.contractInfo, err = w.db.GetContractInfo(addrDesc, bchain.UnknownTokenType)
d.contractInfo, err = w.db.GetContractInfo(addrDesc, bchain.UnknownTokenStandard)
if err != nil {
return nil, nil, err
}
if d.contractInfo != nil && d.contractInfo.Type == bchain.UnhandledTokenType {
d.contractInfo, _, err = w.getContractDescriptorInfo(addrDesc, bchain.UnknownTokenType)
if d.contractInfo != nil && d.contractInfo.Standard == bchain.UnhandledTokenStandard {
d.contractInfo, _, err = w.getContractDescriptorInfo(addrDesc, bchain.UnknownTokenStandard)
if err != nil {
return nil, nil, err
}
Expand Down Expand Up @@ -1467,9 +1470,14 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco
AddressAliases: w.getAddressAliases(addresses),
StakingPools: ed.stakingPools,
}
// keep address backward compatible, set deprecated Erc20Contract value if ERC20 token
if ed.contractInfo != nil && ed.contractInfo.Type == bchain.ERC20TokenType {
r.Erc20Contract = ed.contractInfo
// keep address backward compatible
if ed.contractInfo != nil {
// set deprecated Type value
ed.contractInfo.Type = ed.contractInfo.Standard
// set deprecated Erc20Contract value if ERC20 token
if ed.contractInfo.Standard == bchain.ERC20TokenStandard {
r.Erc20Contract = ed.contractInfo
}
}
glog.Info("GetAddress-", option, " ", address, ", ", time.Since(start))
return r, nil
Expand Down Expand Up @@ -1683,7 +1691,7 @@ func (w *Worker) GetBalanceHistory(address string, fromTimestamp, toTimestamp in
}
// do not get balance history for contracts
if w.chainType == bchain.ChainEthereumType {
ci, err := w.db.GetContractInfo(addrDesc, bchain.UnknownTokenType)
ci, err := w.db.GetContractInfo(addrDesc, bchain.UnknownTokenStandard)
if err != nil {
return nil, err
}
Expand Down
4 changes: 3 additions & 1 deletion api/xpub.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,9 @@ func (w *Worker) tokenFromXpubAddress(data *xpubData, ad *xpubAddress, changeInd
}
}
return Token{
Type: bchain.XPUBAddressTokenType,
// Deprecated: Use Standard instead.
Type: bchain.XPUBAddressStandard,
Standard: bchain.XPUBAddressStandard,
Name: address,
Decimals: w.chainParser.AmountDecimals(),
BalanceSat: (*Amount)(balance),
Expand Down
73 changes: 73 additions & 0 deletions bchain/coins/base/baserpc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package base

import (
"context"
"encoding/json"

"github.com/golang/glog"
"github.com/juju/errors"
"github.com/trezor/blockbook/bchain"
"github.com/trezor/blockbook/bchain/coins/eth"
)

const (
// MainNet is production network
MainNet eth.Network = 8453
)

// BaseRPC is an interface to JSON-RPC base service.
type BaseRPC struct {
*eth.EthereumRPC
}

// NewBaseRPC returns new BaseRPC instance.
func NewBaseRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) {
c, err := eth.NewEthereumRPC(config, pushHandler)
if err != nil {
return nil, err
}

s := &BaseRPC{
EthereumRPC: c.(*eth.EthereumRPC),
}

return s, nil
}

// Initialize base rpc interface
func (b *BaseRPC) Initialize() error {
b.OpenRPC = eth.OpenRPC

rc, ec, err := b.OpenRPC(b.ChainConfig.RPCURL)
if err != nil {
return err
}

// set chain specific
b.Client = ec
b.RPC = rc
b.MainNetChainID = MainNet
b.NewBlock = eth.NewEthereumNewBlock()
b.NewTx = eth.NewEthereumNewTx()

ctx, cancel := context.WithTimeout(context.Background(), b.Timeout)
defer cancel()

id, err := b.Client.NetworkID(ctx)
if err != nil {
return err
}

// parameters for getInfo request
switch eth.Network(id.Uint64()) {
case MainNet:
b.Testnet = false
b.Network = "livenet"
default:
return errors.Errorf("Unknown network id %v", id)
}

glog.Info("rpc: block chain ", b.Network)

return nil
}
3 changes: 3 additions & 0 deletions bchain/coins/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/trezor/blockbook/bchain"
"github.com/trezor/blockbook/bchain/coins/arbitrum"
"github.com/trezor/blockbook/bchain/coins/avalanche"
"github.com/trezor/blockbook/bchain/coins/base"
"github.com/trezor/blockbook/bchain/coins/bch"
"github.com/trezor/blockbook/bchain/coins/bellcoin"
"github.com/trezor/blockbook/bchain/coins/bitcore"
Expand Down Expand Up @@ -147,6 +148,8 @@ func init() {
BlockChainFactories["Arbitrum Archive"] = arbitrum.NewArbitrumRPC
BlockChainFactories["Arbitrum Nova"] = arbitrum.NewArbitrumRPC
BlockChainFactories["Arbitrum Nova Archive"] = arbitrum.NewArbitrumRPC
BlockChainFactories["Base"] = base.NewBaseRPC
BlockChainFactories["Base Archive"] = base.NewBaseRPC
}

// NewBlockChain creates bchain.BlockChain and bchain.Mempool for the coin passed by the parameter coin
Expand Down
Loading