feat: add token list
This commit is contained in:
parent
ecbacb0a7f
commit
5381ec4a76
|
@ -131,26 +131,6 @@ func (api *API) FetchDecodedTxData(ctx context.Context, data string) (*thirdpart
|
||||||
return api.s.decoder.Decode(data)
|
return api.s.decoder.Decode(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// @deprecated
|
|
||||||
// GetTokensBalances return mapping of token balances for every account.
|
|
||||||
func (api *API) GetTokensBalances(ctx context.Context, accounts, addresses []common.Address) (map[common.Address]map[common.Address]*hexutil.Big, error) {
|
|
||||||
chainClients, err := api.s.rpcClient.EthClients([]uint64{api.s.rpcClient.UpstreamChainID})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return api.s.tokenManager.GetBalances(ctx, chainClients, accounts, addresses)
|
|
||||||
}
|
|
||||||
|
|
||||||
// @deprecated
|
|
||||||
func (api *API) GetTokensBalancesForChainIDs(ctx context.Context, chainIDs []uint64, accounts, addresses []common.Address) (map[common.Address]map[common.Address]*hexutil.Big, error) {
|
|
||||||
log.Debug("wallet.api.GetTokensBalances", "accounts", accounts, "addresses", addresses)
|
|
||||||
clients, err := api.s.rpcClient.EthClients(chainIDs)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return api.s.tokenManager.GetBalances(ctx, clients, accounts, addresses)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBalanceHistory retrieves token balance history for token identity on multiple chains
|
// GetBalanceHistory retrieves token balance history for token identity on multiple chains
|
||||||
func (api *API) GetBalanceHistory(ctx context.Context, chainIDs []uint64, address common.Address, tokenSymbol string, currencySymbol string, timeInterval history.TimeInterval) ([]*history.ValuePoint, error) {
|
func (api *API) GetBalanceHistory(ctx context.Context, chainIDs []uint64, address common.Address, tokenSymbol string, currencySymbol string, timeInterval history.TimeInterval) ([]*history.ValuePoint, error) {
|
||||||
log.Debug("wallet.api.GetBalanceHistory", "chainIDs", chainIDs, "address", address, "tokenSymbol", tokenSymbol, "currencySymbol", currencySymbol, "timeInterval", timeInterval)
|
log.Debug("wallet.api.GetBalanceHistory", "chainIDs", chainIDs, "address", address, "tokenSymbol", tokenSymbol, "currencySymbol", currencySymbol, "timeInterval", timeInterval)
|
||||||
|
@ -182,16 +162,25 @@ func (api *API) GetBalanceHistoryRange(ctx context.Context, chainIDs []uint64, a
|
||||||
return api.s.history.GetBalanceHistory(ctx, chainIDs, address, tokenSymbol, currencySymbol, fromTimestamp)
|
return api.s.history.GetBalanceHistory(ctx, chainIDs, address, tokenSymbol, currencySymbol, fromTimestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api *API) GetTokenList(ctx context.Context) ([]*token.List, error) {
|
||||||
|
log.Debug("call to get token list")
|
||||||
|
rst := api.s.tokenManager.GetList()
|
||||||
|
log.Debug("result from token list", "len", len(rst))
|
||||||
|
return rst, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// @deprecated
|
||||||
func (api *API) GetTokens(ctx context.Context, chainID uint64) ([]*token.Token, error) {
|
func (api *API) GetTokens(ctx context.Context, chainID uint64) ([]*token.Token, error) {
|
||||||
log.Debug("call to get tokens")
|
log.Debug("call to get tokens")
|
||||||
rst, err := api.s.tokenManager.GetTokens(chainID, true)
|
rst, err := api.s.tokenManager.GetTokens(chainID)
|
||||||
log.Debug("result from token store", "len", len(rst))
|
log.Debug("result from token store", "len", len(rst))
|
||||||
return rst, err
|
return rst, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @deprecated
|
||||||
func (api *API) GetCustomTokens(ctx context.Context) ([]*token.Token, error) {
|
func (api *API) GetCustomTokens(ctx context.Context) ([]*token.Token, error) {
|
||||||
log.Debug("call to get custom tokens")
|
log.Debug("call to get custom tokens")
|
||||||
rst, err := api.s.tokenManager.GetCustoms()
|
rst, err := api.s.tokenManager.GetCustoms(true)
|
||||||
log.Debug("result from database for custom tokens", "len", len(rst))
|
log.Debug("result from database for custom tokens", "len", len(rst))
|
||||||
return rst, err
|
return rst, err
|
||||||
}
|
}
|
||||||
|
@ -202,24 +191,6 @@ func (api *API) DiscoverToken(ctx context.Context, chainID uint64, address commo
|
||||||
return token, err
|
return token, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// @deprecated
|
|
||||||
func (api *API) GetVisibleTokens(chainIDs []uint64) (map[uint64][]*token.Token, error) {
|
|
||||||
log.Debug("call to get visible tokens")
|
|
||||||
rst, err := api.s.tokenManager.GetVisible(chainIDs)
|
|
||||||
log.Debug("result from database for visible tokens", "len", len(rst))
|
|
||||||
return rst, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// @deprecated
|
|
||||||
func (api *API) ToggleVisibleToken(ctx context.Context, chainID uint64, address common.Address) (bool, error) {
|
|
||||||
log.Debug("call to toggle visible tokens")
|
|
||||||
err := api.s.tokenManager.Toggle(chainID, address)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *API) AddCustomToken(ctx context.Context, token token.Token) error {
|
func (api *API) AddCustomToken(ctx context.Context, token token.Token) error {
|
||||||
log.Debug("call to create or edit custom token")
|
log.Debug("call to create or edit custom token")
|
||||||
if token.ChainID == 0 {
|
if token.ChainID == 0 {
|
||||||
|
|
|
@ -94,7 +94,7 @@ func (s *Service) getAllFiatCurrencyFormats() (FormatPerSymbol, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) fetchAllTokenCurrencyFormats() (FormatPerSymbol, error) {
|
func (s *Service) fetchAllTokenCurrencyFormats() (FormatPerSymbol, error) {
|
||||||
tokens, err := s.tokenManager.GetAllTokensAndNativeCurrencies()
|
tokens, err := s.tokenManager.GetAllTokens()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,17 +6,26 @@ import (
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/appdatabase"
|
||||||
|
"github.com/status-im/status-go/multiaccounts/accounts"
|
||||||
"github.com/status-im/status-go/params"
|
"github.com/status-im/status-go/params"
|
||||||
"github.com/status-im/status-go/rpc"
|
"github.com/status-im/status-go/rpc"
|
||||||
|
"github.com/status-im/status-go/rpc/network"
|
||||||
"github.com/status-im/status-go/t/helpers"
|
"github.com/status-im/status-go/t/helpers"
|
||||||
"github.com/status-im/status-go/walletdatabase"
|
"github.com/status-im/status-go/walletdatabase"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestKeycardPairingsFile(t *testing.T) {
|
func TestKeycardPairingsFile(t *testing.T) {
|
||||||
|
appDB, err := helpers.SetupTestMemorySQLDB(appdatabase.DbInitializer{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
accountsDb, err := accounts.NewDB(appDB)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
db, err := helpers.SetupTestMemorySQLDB(walletdatabase.DbInitializer{})
|
db, err := helpers.SetupTestMemorySQLDB(walletdatabase.DbInitializer{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
service := NewService(db, nil, &rpc.Client{}, nil, nil, nil, nil, ¶ms.NodeConfig{}, nil, nil, nil, nil)
|
service := NewService(db, accountsDb, &rpc.Client{NetworkManager: network.NewManager(db)}, nil, nil, nil, nil, ¶ms.NodeConfig{}, nil, nil, nil, nil)
|
||||||
|
|
||||||
data, err := service.KeycardPairings().GetPairingsJSONFileContent()
|
data, err := service.KeycardPairings().GetPairingsJSONFileContent()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
|
@ -36,7 +36,7 @@ func (p *Persistence) SaveTokens(tokens map[common.Address][]Token) (err error)
|
||||||
if b.HasError || b.Balance.Cmp(big.NewFloat(0)) == 0 {
|
if b.HasError || b.Balance.Cmp(big.NewFloat(0)) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
_, err = tx.Exec(`INSERT INTO token_balances(user_address,token_name,token_symbol,token_address,token_color,token_decimals,token_description,token_url,balance,raw_balance,chain_id) VALUES (?,?,?,?,?,?,?,?,?,?,?)`, address.Hex(), t.Name, t.Symbol, b.Address.Hex(), t.Color, t.Decimals, t.Description, t.AssetWebsiteURL, b.Balance.String(), b.RawBalance, chainID)
|
_, err = tx.Exec(`INSERT INTO token_balances(user_address,token_name,token_symbol,token_address,token_decimals,token_description,token_url,balance,raw_balance,chain_id) VALUES (?,?,?,?,?,?,?,?,?,?)`, address.Hex(), t.Name, t.Symbol, b.Address.Hex(), t.Decimals, t.Description, t.AssetWebsiteURL, b.Balance.String(), b.RawBalance, chainID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ func (p *Persistence) SaveTokens(tokens map[common.Address][]Token) (err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Persistence) GetTokens() (map[common.Address][]Token, error) {
|
func (p *Persistence) GetTokens() (map[common.Address][]Token, error) {
|
||||||
rows, err := p.db.Query(`SELECT user_address, token_name, token_symbol, token_address, token_color, token_decimals, token_description, token_url, balance, raw_balance, chain_id FROM token_balances `)
|
rows, err := p.db.Query(`SELECT user_address, token_name, token_symbol, token_address, token_decimals, token_description, token_url, balance, raw_balance, chain_id FROM token_balances `)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ func (p *Persistence) GetTokens() (map[common.Address][]Token, error) {
|
||||||
token := Token{}
|
token := Token{}
|
||||||
var chainID uint64
|
var chainID uint64
|
||||||
|
|
||||||
err := rows.Scan(&addressStr, &token.Name, &token.Symbol, &tokenAddress, &token.Color, &token.Decimals, &token.Description, &token.AssetWebsiteURL, &balance, &rawBalance, &chainID)
|
err := rows.Scan(&addressStr, &token.Name, &token.Symbol, &tokenAddress, &token.Decimals, &token.Description, &token.AssetWebsiteURL, &balance, &rawBalance, &chainID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,6 @@ func TestSaveTokens(t *testing.T) {
|
||||||
token1 := Token{
|
token1 := Token{
|
||||||
Name: "token-1",
|
Name: "token-1",
|
||||||
Symbol: "TT1",
|
Symbol: "TT1",
|
||||||
Color: "color-1",
|
|
||||||
Decimals: 10,
|
Decimals: 10,
|
||||||
BalancesPerChain: make(map[uint64]ChainBalance),
|
BalancesPerChain: make(map[uint64]ChainBalance),
|
||||||
Description: "description-1",
|
Description: "description-1",
|
||||||
|
@ -58,7 +57,6 @@ func TestSaveTokens(t *testing.T) {
|
||||||
token2 := Token{
|
token2 := Token{
|
||||||
Name: "token-2",
|
Name: "token-2",
|
||||||
Symbol: "TT2",
|
Symbol: "TT2",
|
||||||
Color: "color-2",
|
|
||||||
Decimals: 11,
|
Decimals: 11,
|
||||||
BalancesPerChain: make(map[uint64]ChainBalance),
|
BalancesPerChain: make(map[uint64]ChainBalance),
|
||||||
Description: "description-2",
|
Description: "description-2",
|
||||||
|
@ -75,7 +73,6 @@ func TestSaveTokens(t *testing.T) {
|
||||||
token3 := Token{
|
token3 := Token{
|
||||||
Name: "token-3",
|
Name: "token-3",
|
||||||
Symbol: "TT3",
|
Symbol: "TT3",
|
||||||
Color: "color-3",
|
|
||||||
Decimals: 11,
|
Decimals: 11,
|
||||||
BalancesPerChain: make(map[uint64]ChainBalance),
|
BalancesPerChain: make(map[uint64]ChainBalance),
|
||||||
Description: "description-3",
|
Description: "description-3",
|
||||||
|
@ -118,7 +115,6 @@ func TestSaveTokens(t *testing.T) {
|
||||||
|
|
||||||
require.Equal(t, actualToken1.Name, token1.Name)
|
require.Equal(t, actualToken1.Name, token1.Name)
|
||||||
require.Equal(t, actualToken1.Symbol, token1.Symbol)
|
require.Equal(t, actualToken1.Symbol, token1.Symbol)
|
||||||
require.Equal(t, actualToken1.Color, token1.Color)
|
|
||||||
require.Equal(t, actualToken1.Decimals, token1.Decimals)
|
require.Equal(t, actualToken1.Decimals, token1.Decimals)
|
||||||
require.Equal(t, actualToken1.Description, token1.Description)
|
require.Equal(t, actualToken1.Description, token1.Description)
|
||||||
require.Equal(t, actualToken1.AssetWebsiteURL, token1.AssetWebsiteURL)
|
require.Equal(t, actualToken1.AssetWebsiteURL, token1.AssetWebsiteURL)
|
||||||
|
@ -137,7 +133,6 @@ func TestSaveTokens(t *testing.T) {
|
||||||
|
|
||||||
require.Equal(t, actualToken2.Name, token2.Name)
|
require.Equal(t, actualToken2.Name, token2.Name)
|
||||||
require.Equal(t, actualToken2.Symbol, token2.Symbol)
|
require.Equal(t, actualToken2.Symbol, token2.Symbol)
|
||||||
require.Equal(t, actualToken2.Color, token2.Color)
|
|
||||||
require.Equal(t, actualToken2.Decimals, token2.Decimals)
|
require.Equal(t, actualToken2.Decimals, token2.Decimals)
|
||||||
require.Equal(t, actualToken2.Description, token2.Description)
|
require.Equal(t, actualToken2.Description, token2.Description)
|
||||||
require.Equal(t, actualToken2.AssetWebsiteURL, token2.AssetWebsiteURL)
|
require.Equal(t, actualToken2.AssetWebsiteURL, token2.AssetWebsiteURL)
|
||||||
|
@ -150,7 +145,6 @@ func TestSaveTokens(t *testing.T) {
|
||||||
|
|
||||||
require.Equal(t, actualToken3.Name, token3.Name)
|
require.Equal(t, actualToken3.Name, token3.Name)
|
||||||
require.Equal(t, actualToken3.Symbol, token3.Symbol)
|
require.Equal(t, actualToken3.Symbol, token3.Symbol)
|
||||||
require.Equal(t, actualToken3.Color, token3.Color)
|
|
||||||
require.Equal(t, actualToken3.Decimals, token3.Decimals)
|
require.Equal(t, actualToken3.Decimals, token3.Decimals)
|
||||||
require.Equal(t, actualToken3.Description, token3.Description)
|
require.Equal(t, actualToken3.Description, token3.Description)
|
||||||
require.Equal(t, actualToken3.AssetWebsiteURL, token3.AssetWebsiteURL)
|
require.Equal(t, actualToken3.AssetWebsiteURL, token3.AssetWebsiteURL)
|
||||||
|
|
|
@ -82,7 +82,6 @@ type ChainBalance struct {
|
||||||
type Token struct {
|
type Token struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Symbol string `json:"symbol"`
|
Symbol string `json:"symbol"`
|
||||||
Color string `json:"color"`
|
|
||||||
Decimals uint `json:"decimals"`
|
Decimals uint `json:"decimals"`
|
||||||
BalancesPerChain map[uint64]ChainBalance `json:"balancesPerChain"`
|
BalancesPerChain map[uint64]ChainBalance `json:"balancesPerChain"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
|
@ -192,7 +191,7 @@ func (r *Reader) GetWalletToken(ctx context.Context, addresses []common.Address)
|
||||||
}
|
}
|
||||||
currencies = append(currencies, currency)
|
currencies = append(currencies, currency)
|
||||||
currencies = append(currencies, getFixedCurrencies()...)
|
currencies = append(currencies, getFixedCurrencies()...)
|
||||||
allTokens, err := r.tokenManager.GetTokensByChainIDs(chainIDs, true)
|
allTokens, err := r.tokenManager.GetTokensByChainIDs(chainIDs)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -263,7 +262,6 @@ func (r *Reader) GetWalletToken(ctx context.Context, addresses []common.Address)
|
||||||
|
|
||||||
walletToken := Token{
|
walletToken := Token{
|
||||||
Name: tokens[0].Name,
|
Name: tokens[0].Name,
|
||||||
Color: tokens[0].Color,
|
|
||||||
Symbol: symbol,
|
Symbol: symbol,
|
||||||
BalancesPerChain: balancesPerChain,
|
BalancesPerChain: balancesPerChain,
|
||||||
Decimals: decimals,
|
Decimals: decimals,
|
||||||
|
|
|
@ -31,7 +31,6 @@ var uniswapTokens = []*Token{
|
||||||
Address: common.HexToAddress("{{ $token.Address }}"),
|
Address: common.HexToAddress("{{ $token.Address }}"),
|
||||||
Name: "{{ $token.Name }}",
|
Name: "{{ $token.Name }}",
|
||||||
Symbol: "{{ $token.Symbol }}",
|
Symbol: "{{ $token.Symbol }}",
|
||||||
Color: "{{ $token.Color }}",
|
|
||||||
Decimals: {{ $token.Decimals }},
|
Decimals: {{ $token.Decimals }},
|
||||||
ChainID: {{ $token.ChainID }},
|
ChainID: {{ $token.ChainID }},
|
||||||
PegSymbol: "{{ $token.PegSymbol }}",
|
PegSymbol: "{{ $token.PegSymbol }}",
|
||||||
|
|
|
@ -3,7 +3,6 @@ package token
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -34,7 +33,6 @@ type Token struct {
|
||||||
Address common.Address `json:"address"`
|
Address common.Address `json:"address"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Symbol string `json:"symbol"`
|
Symbol string `json:"symbol"`
|
||||||
Color string `json:"color"`
|
|
||||||
// Decimals defines how divisible the token is. For example, 0 would be
|
// Decimals defines how divisible the token is. For example, 0 would be
|
||||||
// indivisible, whereas 18 would allow very small amounts of the token
|
// indivisible, whereas 18 would allow very small amounts of the token
|
||||||
// to be traded.
|
// to be traded.
|
||||||
|
@ -45,14 +43,26 @@ type Token struct {
|
||||||
// pegged, while "USD" means it's pegged to the United States Dollar.
|
// pegged, while "USD" means it's pegged to the United States Dollar.
|
||||||
PegSymbol string `json:"pegSymbol"`
|
PegSymbol string `json:"pegSymbol"`
|
||||||
|
|
||||||
Verified bool `json:"verified"`
|
|
||||||
CommunityID *string `json:"communityId,omitempty"`
|
CommunityID *string `json:"communityId,omitempty"`
|
||||||
|
Verified bool `json:"verified"`
|
||||||
|
TokenListID string `json:"tokenListId"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Token) IsNative() bool {
|
func (t *Token) IsNative() bool {
|
||||||
return t.Address == nativeChainAddress
|
return t.Address == nativeChainAddress
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type List struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Tokens []*Token `json:"tokens"`
|
||||||
|
UpdatedAt int64 `json:"updatedAt"`
|
||||||
|
Source string `json:"source"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type addressTokenMap = map[common.Address]*Token
|
||||||
|
type storeMap = map[uint64]addressTokenMap
|
||||||
|
|
||||||
type ManagerInterface interface {
|
type ManagerInterface interface {
|
||||||
LookupTokenIdentity(chainID uint64, address common.Address, native bool) *Token
|
LookupTokenIdentity(chainID uint64, address common.Address, native bool) *Token
|
||||||
LookupToken(chainID *uint64, tokenSymbol string) (token *Token, isNative bool)
|
LookupToken(chainID *uint64, tokenSymbol string) (token *Token, isNative bool)
|
||||||
|
@ -66,30 +76,63 @@ type Manager struct {
|
||||||
networkManager *network.Manager
|
networkManager *network.Manager
|
||||||
stores []store // Set on init, not changed afterwards
|
stores []store // Set on init, not changed afterwards
|
||||||
|
|
||||||
// member variables below are protected by mutex
|
tokens []*Token
|
||||||
tokenList []*Token
|
|
||||||
tokenMap storeMap
|
|
||||||
areTokensFetched bool
|
|
||||||
|
|
||||||
tokenLock sync.RWMutex
|
tokenLock sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mergeTokens(sliceLists [][]*Token) []*Token {
|
||||||
|
allKeys := make(map[string]bool)
|
||||||
|
res := []*Token{}
|
||||||
|
for _, list := range sliceLists {
|
||||||
|
for _, token := range list {
|
||||||
|
key := strconv.FormatUint(token.ChainID, 10) + token.Address.String()
|
||||||
|
if _, value := allKeys[key]; !value {
|
||||||
|
allKeys[key] = true
|
||||||
|
res = append(res, token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
func NewTokenManager(
|
func NewTokenManager(
|
||||||
db *sql.DB,
|
db *sql.DB,
|
||||||
RPCClient *rpc.Client,
|
RPCClient *rpc.Client,
|
||||||
networkManager *network.Manager,
|
networkManager *network.Manager,
|
||||||
) *Manager {
|
) *Manager {
|
||||||
maker, _ := contracts.NewContractMaker(RPCClient)
|
maker, _ := contracts.NewContractMaker(RPCClient)
|
||||||
// Order of stores is important when merging token lists. The former prevale
|
stores := []store{newUniswapStore(), newDefaultStore()}
|
||||||
|
tokens := make([]*Token, 0)
|
||||||
|
|
||||||
|
networks, err := networkManager.GetAll()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, store := range stores {
|
||||||
|
validTokens := make([]*Token, 0)
|
||||||
|
for _, token := range store.GetTokens() {
|
||||||
|
token.Verified = true
|
||||||
|
|
||||||
|
for _, network := range networks {
|
||||||
|
if network.ChainID == token.ChainID {
|
||||||
|
validTokens = append(validTokens, token)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens = mergeTokens([][]*Token{tokens, validTokens})
|
||||||
|
}
|
||||||
|
|
||||||
return &Manager{
|
return &Manager{
|
||||||
db: db,
|
db: db,
|
||||||
RPCClient: RPCClient,
|
RPCClient: RPCClient,
|
||||||
contractMaker: maker,
|
contractMaker: maker,
|
||||||
networkManager: networkManager,
|
networkManager: networkManager,
|
||||||
stores: []store{newUniswapStore(), newDefaultStore()},
|
stores: stores,
|
||||||
tokenList: nil,
|
tokens: tokens,
|
||||||
tokenMap: nil,
|
|
||||||
areTokensFetched: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,103 +154,17 @@ func overrideTokensInPlace(networks []params.Network, tokens []*Token) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func mergeTokenLists(sliceLists [][]*Token) []*Token {
|
func (tm *Manager) getTokens() []*Token {
|
||||||
allKeys := make(map[string]bool)
|
|
||||||
res := []*Token{}
|
|
||||||
for _, list := range sliceLists {
|
|
||||||
for _, token := range list {
|
|
||||||
key := strconv.FormatUint(token.ChainID, 10) + token.Address.String()
|
|
||||||
if _, value := allKeys[key]; !value {
|
|
||||||
allKeys[key] = true
|
|
||||||
res = append(res, token)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tm *Manager) inStore(address common.Address, chainID uint64) bool {
|
|
||||||
if address == nativeChainAddress {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if !tm.areTokensFetched {
|
|
||||||
tm.fetchTokens()
|
|
||||||
}
|
|
||||||
|
|
||||||
tokensMap, ok := tm.getAddressTokenMap(chainID)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
_, ok = tokensMap[address]
|
|
||||||
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tm *Manager) getTokenList() []*Token {
|
|
||||||
tm.tokenLock.RLock()
|
tm.tokenLock.RLock()
|
||||||
defer tm.tokenLock.RUnlock()
|
defer tm.tokenLock.RUnlock()
|
||||||
|
|
||||||
return tm.tokenList
|
return tm.tokens
|
||||||
}
|
|
||||||
|
|
||||||
func (tm *Manager) getAddressTokenMap(chainID uint64) (addressTokenMap, bool) {
|
|
||||||
tm.tokenLock.RLock()
|
|
||||||
defer tm.tokenLock.RUnlock()
|
|
||||||
|
|
||||||
tokenMap, chainPresent := tm.tokenMap[chainID]
|
|
||||||
return tokenMap, chainPresent
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *Manager) SetTokens(tokens []*Token) {
|
func (tm *Manager) SetTokens(tokens []*Token) {
|
||||||
tm.tokenLock.Lock()
|
tm.tokenLock.Lock()
|
||||||
defer tm.tokenLock.Unlock()
|
defer tm.tokenLock.Unlock()
|
||||||
|
tm.tokens = tokens
|
||||||
tm.tokenList = tokens
|
|
||||||
tm.tokenMap = toTokenMap(tokens)
|
|
||||||
tm.areTokensFetched = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tm *Manager) fetchTokens() {
|
|
||||||
tokenList := make([]*Token, 0)
|
|
||||||
|
|
||||||
networks, err := tm.networkManager.GetAll()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, store := range tm.stores {
|
|
||||||
tokens := store.GetTokens()
|
|
||||||
validTokens := make([]*Token, 0)
|
|
||||||
for _, token := range tokens {
|
|
||||||
token.Verified = true
|
|
||||||
|
|
||||||
for _, network := range networks {
|
|
||||||
if network.ChainID == token.ChainID {
|
|
||||||
validTokens = append(validTokens, token)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tokenList = mergeTokenLists([][]*Token{tokenList, validTokens})
|
|
||||||
}
|
|
||||||
|
|
||||||
tm.SetTokens(tokenList)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tm *Manager) getFullTokenList(chainID uint64) []*Token {
|
|
||||||
tokens, err := tm.GetTokens(chainID, false)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
customTokens, err := tm.GetCustomsByChainID(chainID, false)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return append(tokens, customTokens...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *Manager) FindToken(network *params.Network, tokenSymbol string) *Token {
|
func (tm *Manager) FindToken(network *params.Network, tokenSymbol string) *Token {
|
||||||
|
@ -220,7 +177,7 @@ func (tm *Manager) FindToken(network *params.Network, tokenSymbol string) *Token
|
||||||
|
|
||||||
func (tm *Manager) LookupToken(chainID *uint64, tokenSymbol string) (token *Token, isNative bool) {
|
func (tm *Manager) LookupToken(chainID *uint64, tokenSymbol string) (token *Token, isNative bool) {
|
||||||
if chainID == nil {
|
if chainID == nil {
|
||||||
networks, err := tm.networkManager.Get(true)
|
networks, err := tm.networkManager.Get(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
@ -246,7 +203,10 @@ func (tm *Manager) LookupToken(chainID *uint64, tokenSymbol string) (token *Toke
|
||||||
|
|
||||||
// GetToken returns token by chainID and tokenSymbol. Use ToToken for native token
|
// GetToken returns token by chainID and tokenSymbol. Use ToToken for native token
|
||||||
func (tm *Manager) GetToken(chainID uint64, tokenSymbol string) *Token {
|
func (tm *Manager) GetToken(chainID uint64, tokenSymbol string) *Token {
|
||||||
allTokens := tm.getFullTokenList(chainID)
|
allTokens, err := tm.GetTokens(chainID)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
for _, token := range allTokens {
|
for _, token := range allTokens {
|
||||||
if token.Symbol == tokenSymbol {
|
if token.Symbol == tokenSymbol {
|
||||||
return token
|
return token
|
||||||
|
@ -265,7 +225,10 @@ func (tm *Manager) LookupTokenIdentity(chainID uint64, address common.Address, n
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *Manager) FindTokenByAddress(chainID uint64, address common.Address) *Token {
|
func (tm *Manager) FindTokenByAddress(chainID uint64, address common.Address) *Token {
|
||||||
allTokens := tm.getFullTokenList(chainID)
|
allTokens, err := tm.GetTokens(chainID)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
for _, token := range allTokens {
|
for _, token := range allTokens {
|
||||||
if token.Address == address {
|
if token.Address == address {
|
||||||
return token
|
return token
|
||||||
|
@ -276,8 +239,23 @@ func (tm *Manager) FindTokenByAddress(chainID uint64, address common.Address) *T
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *Manager) FindOrCreateTokenByAddress(ctx context.Context, chainID uint64, address common.Address) *Token {
|
func (tm *Manager) FindOrCreateTokenByAddress(ctx context.Context, chainID uint64, address common.Address) *Token {
|
||||||
allTokens := tm.getFullTokenList(chainID)
|
// If token comes datasource, simply returns it
|
||||||
for _, token := range allTokens {
|
for _, token := range tm.getTokens() {
|
||||||
|
if token.ChainID != chainID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if token.Address == address {
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create custom token if not known or try to link with a community
|
||||||
|
customTokens, err := tm.GetCustoms(false)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, token := range customTokens {
|
||||||
if token.Address == address {
|
if token.Address == address {
|
||||||
tm.discoverTokenCommunityID(context.Background(), token, address)
|
tm.discoverTokenCommunityID(context.Background(), token, address)
|
||||||
return token
|
return token
|
||||||
|
@ -347,7 +325,7 @@ func (tm *Manager) discoverTokenCommunityID(ctx context.Context, token *Token, a
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *Manager) FindSNT(chainID uint64) *Token {
|
func (tm *Manager) FindSNT(chainID uint64) *Token {
|
||||||
tokens, err := tm.GetTokens(chainID, false)
|
tokens, err := tm.GetTokens(chainID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -361,83 +339,110 @@ func (tm *Manager) FindSNT(chainID uint64) *Token {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *Manager) GetAllTokensAndNativeCurrencies() ([]*Token, error) {
|
func (tm *Manager) getNativeTokens() ([]*Token, error) {
|
||||||
allTokens, err := tm.GetAllTokens()
|
tokens := make([]*Token, 0)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
networks, err := tm.networkManager.Get(false)
|
networks, err := tm.networkManager.Get(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, network := range networks {
|
for _, network := range networks {
|
||||||
allTokens = append(allTokens, tm.ToToken(network))
|
tokens = append(tokens, tm.ToToken(network))
|
||||||
}
|
}
|
||||||
|
|
||||||
return allTokens, nil
|
return tokens, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *Manager) GetAllTokens() ([]*Token, error) {
|
func (tm *Manager) GetAllTokens() ([]*Token, error) {
|
||||||
if !tm.areTokensFetched {
|
allTokens, err := tm.GetCustoms(true)
|
||||||
tm.fetchTokens()
|
|
||||||
}
|
|
||||||
|
|
||||||
tokens, err := tm.GetCustoms()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("can't fetch custom tokens", "error", err)
|
log.Error("can't fetch custom tokens", "error", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tokens = append(tm.getTokenList(), tokens...)
|
allTokens = append(tm.getTokens(), allTokens...)
|
||||||
|
|
||||||
overrideTokensInPlace(tm.networkManager.GetConfiguredNetworks(), tokens)
|
overrideTokensInPlace(tm.networkManager.GetConfiguredNetworks(), allTokens)
|
||||||
|
|
||||||
return tokens, nil
|
native, err := tm.getNativeTokens()
|
||||||
}
|
|
||||||
|
|
||||||
func (tm *Manager) GetTokensByChainIDs(chainIDs []uint64, onlyCommunityCustoms bool) ([]*Token, error) {
|
|
||||||
tokens := make([]*Token, 0)
|
|
||||||
for _, chainID := range chainIDs {
|
|
||||||
t, err := tm.GetTokens(chainID, onlyCommunityCustoms)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
tokens = append(tokens, t...)
|
|
||||||
}
|
allTokens = append(allTokens, native...)
|
||||||
return tokens, nil
|
|
||||||
|
return allTokens, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *Manager) GetDefaultTokens(chainID uint64) ([]*Token, error) {
|
func (tm *Manager) GetTokens(chainID uint64) ([]*Token, error) {
|
||||||
if !tm.areTokensFetched {
|
tokens, err := tm.GetAllTokens()
|
||||||
tm.fetchTokens()
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tokensMap, ok := tm.getAddressTokenMap(chainID)
|
res := make([]*Token, 0)
|
||||||
if !ok {
|
|
||||||
return nil, errors.New("no tokens for this network")
|
|
||||||
}
|
|
||||||
|
|
||||||
res := make([]*Token, 0, len(tokensMap))
|
for _, token := range tokens {
|
||||||
|
if token.ChainID == chainID {
|
||||||
for _, token := range tokensMap {
|
|
||||||
res = append(res, token)
|
res = append(res, token)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *Manager) GetTokens(chainID uint64, onlyCommunityCustoms bool) ([]*Token, error) {
|
func (tm *Manager) GetTokensByChainIDs(chainIDs []uint64) ([]*Token, error) {
|
||||||
res, err := tm.GetDefaultTokens(chainID)
|
tokens, err := tm.GetAllTokens()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tokens, err := tm.GetCustomsByChainID(chainID, onlyCommunityCustoms)
|
res := make([]*Token, 0)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
for _, token := range tokens {
|
||||||
|
for _, chainID := range chainIDs {
|
||||||
|
if token.ChainID == chainID {
|
||||||
|
res = append(res, token)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return append(res, tokens...), nil
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tm *Manager) GetList() []*List {
|
||||||
|
res := make([]*List, 0)
|
||||||
|
nativeTokens, err := tm.getNativeTokens()
|
||||||
|
if err == nil {
|
||||||
|
res = append(res, &List{
|
||||||
|
Name: "native",
|
||||||
|
Tokens: nativeTokens,
|
||||||
|
UpdatedAt: time.Now().Unix(),
|
||||||
|
Source: "native",
|
||||||
|
Version: "1.0.0",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
customTokens, err := tm.GetCustoms(true)
|
||||||
|
if err == nil && len(customTokens) > 0 {
|
||||||
|
res = append(res, &List{
|
||||||
|
Name: "custom",
|
||||||
|
Tokens: customTokens,
|
||||||
|
UpdatedAt: time.Now().Unix(),
|
||||||
|
Source: "custom",
|
||||||
|
Version: "1.0.0",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, store := range tm.stores {
|
||||||
|
res = append(res, &List{
|
||||||
|
Name: store.GetName(),
|
||||||
|
Tokens: store.GetTokens(),
|
||||||
|
UpdatedAt: store.GetUpdatedAt(),
|
||||||
|
Source: store.GetSource(),
|
||||||
|
Version: store.GetVersion(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *Manager) DiscoverToken(ctx context.Context, chainID uint64, address common.Address) (*Token, error) {
|
func (tm *Manager) DiscoverToken(ctx context.Context, chainID uint64, address common.Address) (*Token, error) {
|
||||||
|
@ -476,7 +481,7 @@ func (tm *Manager) DiscoverToken(ctx context.Context, chainID uint64, address co
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *Manager) getTokens(query string, args ...any) ([]*Token, error) {
|
func (tm *Manager) getTokensFromDB(query string, args ...any) ([]*Token, error) {
|
||||||
rows, err := tm.db.Query(query, args...)
|
rows, err := tm.db.Query(query, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -487,7 +492,7 @@ func (tm *Manager) getTokens(query string, args ...any) ([]*Token, error) {
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
token := &Token{}
|
token := &Token{}
|
||||||
var communityIDDB sql.NullString
|
var communityIDDB sql.NullString
|
||||||
err := rows.Scan(&token.Address, &token.Name, &token.Symbol, &token.Decimals, &token.Color, &token.ChainID, &communityIDDB)
|
err := rows.Scan(&token.Address, &token.Name, &token.Symbol, &token.Decimals, &token.ChainID, &communityIDDB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -502,46 +507,11 @@ func (tm *Manager) getTokens(query string, args ...any) ([]*Token, error) {
|
||||||
return rst, nil
|
return rst, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *Manager) GetCustoms() ([]*Token, error) {
|
func (tm *Manager) GetCustoms(onlyCommunityCustoms bool) ([]*Token, error) {
|
||||||
return tm.getTokens("SELECT address, name, symbol, decimals, color, network_id, community_id FROM tokens")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tm *Manager) GetCustomsByChainID(chainID uint64, onlyCommunityCustoms bool) ([]*Token, error) {
|
|
||||||
if onlyCommunityCustoms {
|
if onlyCommunityCustoms {
|
||||||
return tm.getTokens("SELECT address, name, symbol, decimals, color, network_id, community_id FROM tokens WHERE network_id=? AND community_id IS NOT NULL AND community_id != ''", chainID)
|
return tm.getTokensFromDB("SELECT address, name, symbol, decimals, network_id, community_id FROM tokens WHERE community_id IS NOT NULL AND community_id != ''")
|
||||||
}
|
}
|
||||||
return tm.getTokens("SELECT address, name, symbol, decimals, color, network_id, community_id FROM tokens WHERE network_id=?", chainID)
|
return tm.getTokensFromDB("SELECT address, name, symbol, decimals, network_id, community_id FROM tokens")
|
||||||
}
|
|
||||||
|
|
||||||
func (tm *Manager) IsTokenVisible(chainID uint64, address common.Address) (bool, error) {
|
|
||||||
rows, err := tm.db.Query("SELECT chain_id, address FROM visible_tokens WHERE chain_id = ? AND address = ?", chainID, address)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
return rows.Next(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tm *Manager) Toggle(chainID uint64, address common.Address) error {
|
|
||||||
isVisible, err := tm.IsTokenVisible(chainID, address)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if isVisible {
|
|
||||||
_, err = tm.db.Exec(`DELETE FROM visible_tokens WHERE address = ? and chain_id = ?`, address, chainID)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
insert, err := tm.db.Prepare("INSERT OR REPLACE INTO visible_tokens (chain_id, address) VALUES (?, ?)")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer insert.Close()
|
|
||||||
|
|
||||||
_, err = insert.Exec(chainID, address)
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *Manager) ToToken(network *params.Network) *Token {
|
func (tm *Manager) ToToken(network *params.Network) *Token {
|
||||||
|
@ -555,79 +525,12 @@ func (tm *Manager) ToToken(network *params.Network) *Token {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *Manager) GetVisible(chainIDs []uint64) (map[uint64][]*Token, error) {
|
|
||||||
customTokens, err := tm.GetCustoms()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
rst := make(map[uint64][]*Token)
|
|
||||||
for _, chainID := range chainIDs {
|
|
||||||
network := tm.networkManager.Find(chainID)
|
|
||||||
if network == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
rst[chainID] = make([]*Token, 0)
|
|
||||||
rst[chainID] = append(rst[chainID], tm.ToToken(network))
|
|
||||||
}
|
|
||||||
rows, err := tm.db.Query("SELECT chain_id, address FROM visible_tokens")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
for rows.Next() {
|
|
||||||
address := common.HexToAddress("0x")
|
|
||||||
chainID := uint64(0)
|
|
||||||
err := rows.Scan(&chainID, &address)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
found := false
|
|
||||||
tokens, err := tm.GetTokens(chainID, false)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, token := range tokens {
|
|
||||||
if token.Address == address {
|
|
||||||
rst[chainID] = append(rst[chainID], token)
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if found {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, token := range customTokens {
|
|
||||||
if token.Address == address {
|
|
||||||
rst[chainID] = append(rst[chainID], token)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, chainID := range chainIDs {
|
|
||||||
if len(rst[chainID]) == 1 {
|
|
||||||
token := tm.FindSNT(chainID)
|
|
||||||
if token != nil {
|
|
||||||
rst[chainID] = append(rst[chainID], token)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rst, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tm *Manager) UpsertCustom(token Token) error {
|
func (tm *Manager) UpsertCustom(token Token) error {
|
||||||
insert, err := tm.db.Prepare("INSERT OR REPLACE INTO TOKENS (network_id, address, name, symbol, decimals, color) VALUES (?, ?, ?, ?, ?, ?)")
|
insert, err := tm.db.Prepare("INSERT OR REPLACE INTO TOKENS (network_id, address, name, symbol, decimals) VALUES (?, ?, ?, ?, ?)")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = insert.Exec(token.ChainID, token.Address, token.Name, token.Symbol, token.Decimals, token.Color)
|
_, err = insert.Exec(token.ChainID, token.Address, token.Name, token.Symbol, token.Decimals)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -680,136 +583,6 @@ func (tm *Manager) GetBalance(ctx context.Context, client chain.ClientInterface,
|
||||||
return tm.GetTokenBalance(ctx, client, account, token)
|
return tm.GetTokenBalance(ctx, client, account, token)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tm *Manager) GetBalances(parent context.Context, clients map[uint64]chain.ClientInterface, accounts, tokens []common.Address) (map[common.Address]map[common.Address]*hexutil.Big, error) {
|
|
||||||
var (
|
|
||||||
group = async.NewAtomicGroup(parent)
|
|
||||||
mu sync.Mutex
|
|
||||||
response = map[common.Address]map[common.Address]*hexutil.Big{}
|
|
||||||
)
|
|
||||||
|
|
||||||
updateBalance := func(account common.Address, token common.Address, balance *big.Int) {
|
|
||||||
mu.Lock()
|
|
||||||
if _, ok := response[account]; !ok {
|
|
||||||
response[account] = map[common.Address]*hexutil.Big{}
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := response[account][token]; !ok {
|
|
||||||
zeroHex := hexutil.Big(*big.NewInt(0))
|
|
||||||
response[account][token] = &zeroHex
|
|
||||||
}
|
|
||||||
sum := big.NewInt(0).Add(response[account][token].ToInt(), balance)
|
|
||||||
sumHex := hexutil.Big(*sum)
|
|
||||||
response[account][token] = &sumHex
|
|
||||||
mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
for clientIdx := range clients {
|
|
||||||
client := clients[clientIdx]
|
|
||||||
|
|
||||||
ethScanContract, _, err := tm.contractMaker.NewEthScan(client.NetworkID())
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
fetchChainBalance := false
|
|
||||||
var tokenChunks [][]common.Address
|
|
||||||
chunkSize := 500
|
|
||||||
for i := 0; i < len(tokens); i += chunkSize {
|
|
||||||
end := i + chunkSize
|
|
||||||
if end > len(tokens) {
|
|
||||||
end = len(tokens)
|
|
||||||
}
|
|
||||||
|
|
||||||
tokenChunks = append(tokenChunks, tokens[i:end])
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, token := range tokens {
|
|
||||||
if token == nativeChainAddress {
|
|
||||||
fetchChainBalance = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if fetchChainBalance {
|
|
||||||
group.Add(func(parent context.Context) error {
|
|
||||||
ctx, cancel := context.WithTimeout(parent, requestTimeout)
|
|
||||||
defer cancel()
|
|
||||||
res, err := ethScanContract.EtherBalances(&bind.CallOpts{
|
|
||||||
Context: ctx,
|
|
||||||
}, accounts)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("can't fetch chain balance 2", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
for idx, account := range accounts {
|
|
||||||
balance := new(big.Int)
|
|
||||||
balance.SetBytes(res[idx].Data)
|
|
||||||
updateBalance(account, common.HexToAddress("0x"), balance)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
for accountIdx := range accounts {
|
|
||||||
account := accounts[accountIdx]
|
|
||||||
for idx := range tokenChunks {
|
|
||||||
chunk := tokenChunks[idx]
|
|
||||||
group.Add(func(parent context.Context) error {
|
|
||||||
ctx, cancel := context.WithTimeout(parent, requestTimeout)
|
|
||||||
defer cancel()
|
|
||||||
res, err := ethScanContract.TokensBalance(&bind.CallOpts{
|
|
||||||
Context: ctx,
|
|
||||||
}, account, chunk)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("can't fetch erc20 token balance 3", "account", account, "error", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for idx, token := range chunk {
|
|
||||||
if !res[idx].Success {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
balance := new(big.Int)
|
|
||||||
balance.SetBytes(res[idx].Data)
|
|
||||||
updateBalance(account, token, balance)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for tokenIdx := range tokens {
|
|
||||||
for accountIdx := range accounts {
|
|
||||||
// Below, we set account, token and client from idx on purpose to avoid override
|
|
||||||
account := accounts[accountIdx]
|
|
||||||
token := tokens[tokenIdx]
|
|
||||||
client := clients[clientIdx]
|
|
||||||
if !tm.inStore(token, client.NetworkID()) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
group.Add(func(parent context.Context) error {
|
|
||||||
ctx, cancel := context.WithTimeout(parent, requestTimeout)
|
|
||||||
defer cancel()
|
|
||||||
balance, err := tm.GetBalance(ctx, client, account, token)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Error("can't fetch erc20 token balance 4", "account", account, "token", token, "error", err)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
updateBalance(account, token, balance)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-group.WaitAsync():
|
|
||||||
case <-parent.Done():
|
|
||||||
return nil, parent.Err()
|
|
||||||
}
|
|
||||||
return response, group.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tm *Manager) GetBalancesByChain(parent context.Context, clients map[uint64]chain.ClientInterface, accounts, tokens []common.Address) (map[uint64]map[common.Address]map[common.Address]*hexutil.Big, error) {
|
func (tm *Manager) GetBalancesByChain(parent context.Context, clients map[uint64]chain.ClientInterface, accounts, tokens []common.Address) (map[uint64]map[common.Address]map[common.Address]*hexutil.Big, error) {
|
||||||
return tm.GetBalancesAtByChain(parent, clients, accounts, tokens, nil)
|
return tm.GetBalancesAtByChain(parent, clients, accounts, tokens, nil)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package token
|
package token
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -23,9 +22,6 @@ func setupTestTokenDB(t *testing.T) (*Manager, func()) {
|
||||||
contractMaker: nil,
|
contractMaker: nil,
|
||||||
networkManager: nil,
|
networkManager: nil,
|
||||||
stores: nil,
|
stores: nil,
|
||||||
tokenList: nil,
|
|
||||||
tokenMap: nil,
|
|
||||||
areTokensFetched: false,
|
|
||||||
}, func() {
|
}, func() {
|
||||||
require.NoError(t, db.Close())
|
require.NoError(t, db.Close())
|
||||||
}
|
}
|
||||||
|
@ -35,7 +31,7 @@ func TestCustoms(t *testing.T) {
|
||||||
manager, stop := setupTestTokenDB(t)
|
manager, stop := setupTestTokenDB(t)
|
||||||
defer stop()
|
defer stop()
|
||||||
|
|
||||||
rst, err := manager.GetCustoms()
|
rst, err := manager.GetCustoms(false)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Nil(t, rst)
|
require.Nil(t, rst)
|
||||||
|
|
||||||
|
@ -44,14 +40,13 @@ func TestCustoms(t *testing.T) {
|
||||||
Name: "Zilliqa",
|
Name: "Zilliqa",
|
||||||
Symbol: "ZIL",
|
Symbol: "ZIL",
|
||||||
Decimals: 12,
|
Decimals: 12,
|
||||||
Color: "#fa6565",
|
|
||||||
ChainID: 777,
|
ChainID: 777,
|
||||||
}
|
}
|
||||||
|
|
||||||
err = manager.UpsertCustom(token)
|
err = manager.UpsertCustom(token)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
rst, err = manager.GetCustoms()
|
rst, err = manager.GetCustoms(false)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 1, len(rst))
|
require.Equal(t, 1, len(rst))
|
||||||
require.Equal(t, token, *rst[0])
|
require.Equal(t, token, *rst[0])
|
||||||
|
@ -59,11 +54,27 @@ func TestCustoms(t *testing.T) {
|
||||||
err = manager.DeleteCustom(777, token.Address)
|
err = manager.DeleteCustom(777, token.Address)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
rst, err = manager.GetCustoms()
|
rst, err = manager.GetCustoms(false)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 0, len(rst))
|
require.Equal(t, 0, len(rst))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func toTokenMap(tokens []*Token) storeMap {
|
||||||
|
tokenMap := storeMap{}
|
||||||
|
|
||||||
|
for _, token := range tokens {
|
||||||
|
addTokMap := tokenMap[token.ChainID]
|
||||||
|
if addTokMap == nil {
|
||||||
|
addTokMap = make(addressTokenMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
addTokMap[token.Address] = token
|
||||||
|
tokenMap[token.ChainID] = addTokMap
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokenMap
|
||||||
|
}
|
||||||
|
|
||||||
func TestTokenOverride(t *testing.T) {
|
func TestTokenOverride(t *testing.T) {
|
||||||
networks := []params.Network{
|
networks := []params.Network{
|
||||||
{
|
{
|
||||||
|
@ -113,7 +124,7 @@ func TestTokenOverride(t *testing.T) {
|
||||||
tokenList,
|
tokenList,
|
||||||
}
|
}
|
||||||
|
|
||||||
overrideTokensInPlace(networks, testStore.tokenList)
|
overrideTokensInPlace(networks, tokenList)
|
||||||
tokens := testStore.GetTokens()
|
tokens := testStore.GetTokens()
|
||||||
tokenMap := toTokenMap(tokens)
|
tokenMap := toTokenMap(tokens)
|
||||||
_, found := tokenMap[1][common.Address{1}]
|
_, found := tokenMap[1][common.Address{1}]
|
||||||
|
@ -125,62 +136,3 @@ func TestTokenOverride(t *testing.T) {
|
||||||
require.Equal(t, common.Address{33}, tokenMap[2][common.Address{33}].Address)
|
require.Equal(t, common.Address{33}, tokenMap[2][common.Address{33}].Address)
|
||||||
require.Equal(t, common.Address{4}, tokenMap[2][common.Address{4}].Address)
|
require.Equal(t, common.Address{4}, tokenMap[2][common.Address{4}].Address)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMergeTokenLists(t *testing.T) {
|
|
||||||
tokenList1 := []*Token{
|
|
||||||
&Token{
|
|
||||||
Address: common.Address{1},
|
|
||||||
Symbol: "SNT",
|
|
||||||
ChainID: 1,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
tokenList1Copy := []*Token{
|
|
||||||
&Token{
|
|
||||||
Address: common.Address{1},
|
|
||||||
Symbol: "SNT",
|
|
||||||
ChainID: 1,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
tokenList2 := []*Token{
|
|
||||||
&Token{
|
|
||||||
Address: common.Address{3},
|
|
||||||
Symbol: "STT",
|
|
||||||
ChainID: 2,
|
|
||||||
},
|
|
||||||
&Token{
|
|
||||||
Address: common.Address{4},
|
|
||||||
Symbol: "TTT",
|
|
||||||
ChainID: 2,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
tokenList1Plus2 := []*Token{
|
|
||||||
&Token{
|
|
||||||
Address: common.Address{1},
|
|
||||||
Symbol: "SNT",
|
|
||||||
ChainID: 1,
|
|
||||||
},
|
|
||||||
&Token{
|
|
||||||
Address: common.Address{3},
|
|
||||||
Symbol: "STT",
|
|
||||||
ChainID: 2,
|
|
||||||
},
|
|
||||||
&Token{
|
|
||||||
Address: common.Address{4},
|
|
||||||
Symbol: "TTT",
|
|
||||||
ChainID: 2,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
tokenListEmpty := []*Token{}
|
|
||||||
|
|
||||||
mergedList := mergeTokenLists([][]*Token{tokenListEmpty, tokenListEmpty})
|
|
||||||
require.Equal(t, 0, len(mergedList))
|
|
||||||
|
|
||||||
mergedList = mergeTokenLists([][]*Token{tokenListEmpty, tokenList1})
|
|
||||||
require.True(t, reflect.DeepEqual(mergedList, tokenList1))
|
|
||||||
|
|
||||||
mergedList = mergeTokenLists([][]*Token{tokenList1, tokenList1Copy})
|
|
||||||
require.True(t, reflect.DeepEqual(mergedList, tokenList1))
|
|
||||||
|
|
||||||
mergedList = mergeTokenLists([][]*Token{tokenList1, tokenList2})
|
|
||||||
require.True(t, reflect.DeepEqual(mergedList, tokenList1Plus2))
|
|
||||||
}
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -7,6 +7,26 @@ func newUniswapStore() *uniswapStore {
|
||||||
return &uniswapStore{}
|
return &uniswapStore{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ts *uniswapStore) GetTokens() []*Token {
|
func (s *uniswapStore) GetTokens() []*Token {
|
||||||
|
for _, token := range uniswapTokens {
|
||||||
|
token.TokenListID = "uniswap"
|
||||||
|
}
|
||||||
|
|
||||||
return uniswapTokens
|
return uniswapTokens
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *uniswapStore) GetName() string {
|
||||||
|
return "Uniswap Labs Default Token List"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *uniswapStore) GetVersion() string {
|
||||||
|
return "11.8.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *uniswapStore) GetUpdatedAt() int64 {
|
||||||
|
return 1697613003
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *uniswapStore) GetSource() string {
|
||||||
|
return "https://gateway.ipfs.io/ipns/tokens.uniswap.org"
|
||||||
|
}
|
||||||
|
|
|
@ -164,7 +164,7 @@ func (c *findBlocksCommand) ERC20ScanByBalance(parent context.Context, fromBlock
|
||||||
|
|
||||||
func (c *findBlocksCommand) checkERC20Tail(parent context.Context) ([]*DBHeader, error) {
|
func (c *findBlocksCommand) checkERC20Tail(parent context.Context) ([]*DBHeader, error) {
|
||||||
log.Debug("checkERC20Tail", "account", c.account, "to block", c.startBlockNumber, "from", c.resFromBlock.Number)
|
log.Debug("checkERC20Tail", "account", c.account, "to block", c.startBlockNumber, "from", c.resFromBlock.Number)
|
||||||
tokens, err := c.tokenManager.GetTokens(c.chainClient.NetworkID(), false)
|
tokens, err := c.tokenManager.GetTokens(c.chainClient.NetworkID())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
"github.com/status-im/status-go/appdatabase"
|
||||||
"github.com/status-im/status-go/contracts/ethscan"
|
"github.com/status-im/status-go/contracts/ethscan"
|
||||||
"github.com/status-im/status-go/contracts/ierc20"
|
"github.com/status-im/status-go/contracts/ierc20"
|
||||||
"github.com/status-im/status-go/rpc/chain"
|
"github.com/status-im/status-go/rpc/chain"
|
||||||
|
@ -867,6 +868,9 @@ func TestFindBlocksCommand(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
group := async.NewGroup(ctx)
|
group := async.NewGroup(ctx)
|
||||||
|
|
||||||
|
appdb, err := helpers.SetupTestMemorySQLDB(appdatabase.DbInitializer{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
db, err := helpers.SetupTestMemorySQLDB(walletdatabase.DbInitializer{})
|
db, err := helpers.SetupTestMemorySQLDB(walletdatabase.DbInitializer{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
tm := &TransactionManager{db, nil, nil, nil, nil, nil, nil, nil, nil, nil}
|
tm := &TransactionManager{db, nil, nil, nil, nil, nil, nil, nil, nil, nil}
|
||||||
|
@ -892,7 +896,7 @@ func TestFindBlocksCommand(t *testing.T) {
|
||||||
}
|
}
|
||||||
client, _ := statusRpc.NewClient(nil, 1, params.UpstreamRPCConfig{Enabled: false, URL: ""}, []params.Network{}, db)
|
client, _ := statusRpc.NewClient(nil, 1, params.UpstreamRPCConfig{Enabled: false, URL: ""}, []params.Network{}, db)
|
||||||
client.SetClient(tc.NetworkID(), tc)
|
client.SetClient(tc.NetworkID(), tc)
|
||||||
tokenManager := token.NewTokenManager(db, client, network.NewManager(db))
|
tokenManager := token.NewTokenManager(db, client, network.NewManager(appdb))
|
||||||
tokenManager.SetTokens([]*token.Token{
|
tokenManager.SetTokens([]*token.Token{
|
||||||
{
|
{
|
||||||
Address: tokenTXXAddress,
|
Address: tokenTXXAddress,
|
||||||
|
@ -990,6 +994,9 @@ func (m *MockChainClient) AbstractEthClient(chainID walletcommon.ChainID) (chain
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFetchTransfersForLoadedBlocks(t *testing.T) {
|
func TestFetchTransfersForLoadedBlocks(t *testing.T) {
|
||||||
|
appdb, err := helpers.SetupTestMemorySQLDB(appdatabase.DbInitializer{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
db, err := helpers.SetupTestMemorySQLDB(walletdatabase.DbInitializer{})
|
db, err := helpers.SetupTestMemorySQLDB(walletdatabase.DbInitializer{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
tm := &TransactionManager{db, nil, nil, nil, nil, nil, nil, nil, nil, nil}
|
tm := &TransactionManager{db, nil, nil, nil, nil, nil, nil, nil, nil, nil}
|
||||||
|
@ -1008,7 +1015,7 @@ func TestFetchTransfersForLoadedBlocks(t *testing.T) {
|
||||||
|
|
||||||
client, _ := statusRpc.NewClient(nil, 1, params.UpstreamRPCConfig{Enabled: false, URL: ""}, []params.Network{}, db)
|
client, _ := statusRpc.NewClient(nil, 1, params.UpstreamRPCConfig{Enabled: false, URL: ""}, []params.Network{}, db)
|
||||||
client.SetClient(tc.NetworkID(), tc)
|
client.SetClient(tc.NetworkID(), tc)
|
||||||
tokenManager := token.NewTokenManager(db, client, network.NewManager(db))
|
tokenManager := token.NewTokenManager(db, client, network.NewManager(appdb))
|
||||||
|
|
||||||
tokenManager.SetTokens([]*token.Token{
|
tokenManager.SetTokens([]*token.Token{
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue