feat: create unknown erc20
This commit is contained in:
parent
555aae4d0f
commit
22fc83de59
|
@ -90,6 +90,22 @@ type Token struct {
|
||||||
BuiltOn string `json:"builtOn"`
|
BuiltOn string `json:"builtOn"`
|
||||||
MarketValuesPerCurrency map[string]TokenMarketValues `json:"marketValuesPerCurrency"`
|
MarketValuesPerCurrency map[string]TokenMarketValues `json:"marketValuesPerCurrency"`
|
||||||
PegSymbol string `json:"pegSymbol"`
|
PegSymbol string `json:"pegSymbol"`
|
||||||
|
Verified bool `json:"verified"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitVerifiedTokens(tokens []*token.Token) ([]*token.Token, []*token.Token) {
|
||||||
|
verified := make([]*token.Token, 0)
|
||||||
|
unverified := make([]*token.Token, 0)
|
||||||
|
|
||||||
|
for _, t := range tokens {
|
||||||
|
if t.Verified {
|
||||||
|
verified = append(verified, t)
|
||||||
|
} else {
|
||||||
|
unverified = append(unverified, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return verified, unverified
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTokenBySymbols(tokens []*token.Token) map[string][]*token.Token {
|
func getTokenBySymbols(tokens []*token.Token) map[string][]*token.Token {
|
||||||
|
@ -184,8 +200,8 @@ 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)
|
allTokens, err := r.tokenManager.GetTokensByChainIDs(chainIDs)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -251,68 +267,73 @@ func (r *Reader) GetWalletToken(ctx context.Context, addresses []common.Address)
|
||||||
}
|
}
|
||||||
err = group.Error()
|
err = group.Error()
|
||||||
result := make(map[common.Address][]Token)
|
result := make(map[common.Address][]Token)
|
||||||
|
verifiedTokens, unverifiedTokens := splitVerifiedTokens(allTokens)
|
||||||
|
|
||||||
for _, address := range addresses {
|
for _, address := range addresses {
|
||||||
for symbol, tokens := range getTokenBySymbols(allTokens) {
|
for _, tokenList := range [][]*token.Token{verifiedTokens, unverifiedTokens} {
|
||||||
balancesPerChain := make(map[uint64]ChainBalance)
|
for symbol, tokens := range getTokenBySymbols(tokenList) {
|
||||||
decimals := tokens[0].Decimals
|
balancesPerChain := make(map[uint64]ChainBalance)
|
||||||
anyPositiveBalance := false
|
decimals := tokens[0].Decimals
|
||||||
for _, token := range tokens {
|
anyPositiveBalance := false
|
||||||
hexBalance := balances[token.ChainID][address][token.Address]
|
for _, token := range tokens {
|
||||||
balance := big.NewFloat(0.0)
|
hexBalance := balances[token.ChainID][address][token.Address]
|
||||||
if hexBalance != nil {
|
balance := big.NewFloat(0.0)
|
||||||
balance = new(big.Float).Quo(
|
if hexBalance != nil {
|
||||||
new(big.Float).SetInt(hexBalance.ToInt()),
|
balance = new(big.Float).Quo(
|
||||||
big.NewFloat(math.Pow(10, float64(decimals))),
|
new(big.Float).SetInt(hexBalance.ToInt()),
|
||||||
)
|
big.NewFloat(math.Pow(10, float64(decimals))),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
hasError := false
|
||||||
|
if client, ok := clients[token.ChainID]; ok {
|
||||||
|
hasError = err != nil || !client.IsConnected
|
||||||
|
}
|
||||||
|
if !anyPositiveBalance {
|
||||||
|
anyPositiveBalance = balance.Cmp(big.NewFloat(0.0)) > 0
|
||||||
|
}
|
||||||
|
balancesPerChain[token.ChainID] = ChainBalance{
|
||||||
|
Balance: balance,
|
||||||
|
Address: token.Address,
|
||||||
|
ChainID: token.ChainID,
|
||||||
|
HasError: hasError,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
hasError := false
|
|
||||||
if client, ok := clients[token.ChainID]; ok {
|
|
||||||
hasError = err != nil || !client.IsConnected
|
|
||||||
}
|
|
||||||
if !anyPositiveBalance {
|
|
||||||
anyPositiveBalance = balance.Cmp(big.NewFloat(0.0)) > 0
|
|
||||||
}
|
|
||||||
balancesPerChain[token.ChainID] = ChainBalance{
|
|
||||||
Balance: balance,
|
|
||||||
Address: token.Address,
|
|
||||||
ChainID: token.ChainID,
|
|
||||||
HasError: hasError,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !anyPositiveBalance && !belongsToMandatoryTokens(symbol) {
|
if !anyPositiveBalance && !belongsToMandatoryTokens(symbol) {
|
||||||
continue
|
continue
|
||||||
}
|
|
||||||
|
|
||||||
marketValuesPerCurrency := make(map[string]TokenMarketValues)
|
|
||||||
for _, currency := range currencies {
|
|
||||||
marketValuesPerCurrency[currency] = TokenMarketValues{
|
|
||||||
MarketCap: tokenMarketValues[symbol].MKTCAP,
|
|
||||||
HighDay: tokenMarketValues[symbol].HIGHDAY,
|
|
||||||
LowDay: tokenMarketValues[symbol].LOWDAY,
|
|
||||||
ChangePctHour: tokenMarketValues[symbol].CHANGEPCTHOUR,
|
|
||||||
ChangePctDay: tokenMarketValues[symbol].CHANGEPCTDAY,
|
|
||||||
ChangePct24hour: tokenMarketValues[symbol].CHANGEPCT24HOUR,
|
|
||||||
Change24hour: tokenMarketValues[symbol].CHANGE24HOUR,
|
|
||||||
Price: prices[symbol][currency],
|
|
||||||
HasError: !r.marketManager.IsConnected,
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
walletToken := Token{
|
marketValuesPerCurrency := make(map[string]TokenMarketValues)
|
||||||
Name: tokens[0].Name,
|
for _, currency := range currencies {
|
||||||
Color: tokens[0].Color,
|
marketValuesPerCurrency[currency] = TokenMarketValues{
|
||||||
Symbol: symbol,
|
MarketCap: tokenMarketValues[symbol].MKTCAP,
|
||||||
BalancesPerChain: balancesPerChain,
|
HighDay: tokenMarketValues[symbol].HIGHDAY,
|
||||||
Decimals: decimals,
|
LowDay: tokenMarketValues[symbol].LOWDAY,
|
||||||
Description: tokenDetails[symbol].Description,
|
ChangePctHour: tokenMarketValues[symbol].CHANGEPCTHOUR,
|
||||||
AssetWebsiteURL: tokenDetails[symbol].AssetWebsiteURL,
|
ChangePctDay: tokenMarketValues[symbol].CHANGEPCTDAY,
|
||||||
BuiltOn: tokenDetails[symbol].BuiltOn,
|
ChangePct24hour: tokenMarketValues[symbol].CHANGEPCT24HOUR,
|
||||||
MarketValuesPerCurrency: marketValuesPerCurrency,
|
Change24hour: tokenMarketValues[symbol].CHANGE24HOUR,
|
||||||
PegSymbol: token.GetTokenPegSymbol(symbol),
|
Price: prices[symbol][currency],
|
||||||
}
|
HasError: !r.marketManager.IsConnected,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
result[address] = append(result[address], walletToken)
|
walletToken := Token{
|
||||||
|
Name: tokens[0].Name,
|
||||||
|
Color: tokens[0].Color,
|
||||||
|
Symbol: symbol,
|
||||||
|
BalancesPerChain: balancesPerChain,
|
||||||
|
Decimals: decimals,
|
||||||
|
Description: tokenDetails[symbol].Description,
|
||||||
|
AssetWebsiteURL: tokenDetails[symbol].AssetWebsiteURL,
|
||||||
|
BuiltOn: tokenDetails[symbol].BuiltOn,
|
||||||
|
MarketValuesPerCurrency: marketValuesPerCurrency,
|
||||||
|
PegSymbol: token.GetTokenPegSymbol(symbol),
|
||||||
|
Verified: tokens[0].Verified,
|
||||||
|
}
|
||||||
|
|
||||||
|
result[address] = append(result[address], walletToken)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,8 @@ type Token struct {
|
||||||
// ISO 4217 alphabetic code. For example, an empty string means it is not
|
// ISO 4217 alphabetic code. For example, an empty string means it is not
|
||||||
// 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"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Token) IsNative() bool {
|
func (t *Token) IsNative() bool {
|
||||||
|
@ -54,6 +56,7 @@ type ManagerInterface interface {
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
db *sql.DB
|
db *sql.DB
|
||||||
RPCClient *rpc.Client
|
RPCClient *rpc.Client
|
||||||
|
contractMaker *contracts.ContractMaker
|
||||||
networkManager *network.Manager
|
networkManager *network.Manager
|
||||||
stores []store
|
stores []store
|
||||||
tokenList []*Token
|
tokenList []*Token
|
||||||
|
@ -66,9 +69,18 @@ func NewTokenManager(
|
||||||
RPCClient *rpc.Client,
|
RPCClient *rpc.Client,
|
||||||
networkManager *network.Manager,
|
networkManager *network.Manager,
|
||||||
) *Manager {
|
) *Manager {
|
||||||
|
maker, _ := contracts.NewContractMaker(RPCClient)
|
||||||
// Order of stores is important when merging token lists. The former prevale
|
// Order of stores is important when merging token lists. The former prevale
|
||||||
tokenManager := &Manager{db, RPCClient, networkManager, []store{newUniswapStore(), newDefaultStore()}, nil, nil, false}
|
return &Manager{
|
||||||
return tokenManager
|
db,
|
||||||
|
RPCClient,
|
||||||
|
maker,
|
||||||
|
networkManager,
|
||||||
|
[]store{newUniswapStore(), newDefaultStore()},
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// overrideTokensInPlace overrides tokens in the store with the ones from the networks
|
// overrideTokensInPlace overrides tokens in the store with the ones from the networks
|
||||||
|
@ -135,6 +147,8 @@ func (tm *Manager) fetchTokens() {
|
||||||
tokens := store.GetTokens()
|
tokens := store.GetTokens()
|
||||||
validTokens := make([]*Token, 0)
|
validTokens := make([]*Token, 0)
|
||||||
for _, token := range tokens {
|
for _, token := range tokens {
|
||||||
|
token.Verified = true
|
||||||
|
|
||||||
for _, network := range networks {
|
for _, network := range networks {
|
||||||
if network.ChainID == token.ChainID {
|
if network.ChainID == token.ChainID {
|
||||||
validTokens = append(validTokens, token)
|
validTokens = append(validTokens, token)
|
||||||
|
@ -224,9 +238,31 @@ func (tm *Manager) FindTokenByAddress(chainID uint64, address common.Address) *T
|
||||||
return token
|
return token
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tm *Manager) FindOrCreateTokenByAddress(ctx context.Context, chainID uint64, address common.Address) *Token {
|
||||||
|
allTokens := tm.getFullTokenList(chainID)
|
||||||
|
for _, token := range allTokens {
|
||||||
|
if token.Address == address {
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := tm.DiscoverToken(ctx, chainID, address)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tm.UpsertCustom(*token)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
|
||||||
func (tm *Manager) FindSNT(chainID uint64) *Token {
|
func (tm *Manager) FindSNT(chainID uint64) *Token {
|
||||||
tokens, err := tm.GetTokens(chainID)
|
tokens, err := tm.GetTokens(chainID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -248,7 +284,7 @@ func (tm *Manager) GetAllTokensAndNativeCurrencies() ([]*Token, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
networks, err := tm.RPCClient.NetworkManager.Get(false)
|
networks, err := tm.networkManager.Get(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -314,11 +350,7 @@ func (tm *Manager) GetTokens(chainID uint64) ([]*Token, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
backend, err := tm.RPCClient.EthClient(chainID)
|
caller, err := tm.contractMaker.NewERC20(chainID, address)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
caller, err := ierc20.NewIERC20Caller(address, backend)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -349,6 +381,7 @@ func (tm *Manager) DiscoverToken(ctx context.Context, chainID uint64, address co
|
||||||
Name: name,
|
Name: name,
|
||||||
Symbol: symbol,
|
Symbol: symbol,
|
||||||
Decimals: uint(decimal),
|
Decimals: uint(decimal),
|
||||||
|
ChainID: chainID,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -432,6 +465,7 @@ func (tm *Manager) ToToken(network *params.Network) *Token {
|
||||||
Symbol: network.NativeCurrencySymbol,
|
Symbol: network.NativeCurrencySymbol,
|
||||||
Decimals: uint(network.NativeCurrencyDecimals),
|
Decimals: uint(network.NativeCurrencyDecimals),
|
||||||
ChainID: network.ChainID,
|
ChainID: network.ChainID,
|
||||||
|
Verified: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -573,14 +607,11 @@ func (tm *Manager) GetBalances(parent context.Context, clients map[uint64]*chain
|
||||||
response[account][token] = &sumHex
|
response[account][token] = &sumHex
|
||||||
mu.Unlock()
|
mu.Unlock()
|
||||||
}
|
}
|
||||||
contractMaker, err := contracts.NewContractMaker(tm.RPCClient)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for clientIdx := range clients {
|
for clientIdx := range clients {
|
||||||
client := clients[clientIdx]
|
client := clients[clientIdx]
|
||||||
|
|
||||||
ethScanContract, err := contractMaker.NewEthScan(client.ChainID)
|
ethScanContract, err := tm.contractMaker.NewEthScan(client.ChainID)
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
fetchChainBalance := false
|
fetchChainBalance := false
|
||||||
|
@ -711,13 +742,9 @@ func (tm *Manager) GetBalancesByChain(parent context.Context, clients map[uint64
|
||||||
mu.Unlock()
|
mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
contractMaker, err := contracts.NewContractMaker(tm.RPCClient)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for clientIdx := range clients {
|
for clientIdx := range clients {
|
||||||
client := clients[clientIdx]
|
client := clients[clientIdx]
|
||||||
ethScanContract, err := contractMaker.NewEthScan(client.ChainID)
|
ethScanContract, err := tm.contractMaker.NewEthScan(client.ChainID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("error scanning contract", "err", err)
|
log.Error("error scanning contract", "err", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -16,7 +16,7 @@ import (
|
||||||
func setupTestTokenDB(t *testing.T) (*Manager, func()) {
|
func setupTestTokenDB(t *testing.T) (*Manager, func()) {
|
||||||
db, err := helpers.SetupTestMemorySQLDB(walletdatabase.DbInitializer{})
|
db, err := helpers.SetupTestMemorySQLDB(walletdatabase.DbInitializer{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
return &Manager{db, nil, nil, nil, nil, nil, false}, func() {
|
return &Manager{db, nil, nil, nil, nil, nil, nil, false}, func() {
|
||||||
require.NoError(t, db.Close())
|
require.NoError(t, db.Close())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -404,6 +404,8 @@ func (c *transfersCommand) Run(ctx context.Context) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.processUnknownErc20CommunityTransactions(ctx, allTransfers)
|
||||||
|
|
||||||
err = c.processMultiTransactions(ctx, allTransfers)
|
err = c.processMultiTransactions(ctx, allTransfers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("processMultiTransactions error", "error", err)
|
log.Error("processMultiTransactions error", "error", err)
|
||||||
|
@ -548,6 +550,15 @@ func (c *transfersCommand) checkAndProcessBridgeMultiTx(ctx context.Context, tx
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *transfersCommand) processUnknownErc20CommunityTransactions(ctx context.Context, allTransfers []Transfer) {
|
||||||
|
for _, tx := range allTransfers {
|
||||||
|
if tx.Type == w_common.Erc20Transfer {
|
||||||
|
// Find token in db or if this is a community token, find its metadata
|
||||||
|
_ = c.tokenManager.FindOrCreateTokenByAddress(ctx, tx.NetworkID, *tx.Transaction.To())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *transfersCommand) processMultiTransactions(ctx context.Context, allTransfers []Transfer) error {
|
func (c *transfersCommand) processMultiTransactions(ctx context.Context, allTransfers []Transfer) error {
|
||||||
txByTxHash := subTransactionListToTransactionsByTxHash(allTransfers)
|
txByTxHash := subTransactionListToTransactionsByTxHash(allTransfers)
|
||||||
|
|
||||||
|
|
|
@ -317,7 +317,6 @@ func (d *ETHDownloader) subTransactionsFromTransactionData(tx *types.Transaction
|
||||||
}
|
}
|
||||||
|
|
||||||
rst := make([]Transfer, 0, len(receipt.Logs))
|
rst := make([]Transfer, 0, len(receipt.Logs))
|
||||||
|
|
||||||
for _, log := range receipt.Logs {
|
for _, log := range receipt.Logs {
|
||||||
eventType := w_common.GetEventType(log)
|
eventType := w_common.GetEventType(log)
|
||||||
// Only add ERC20/ERC721 transfers from/to the given account
|
// Only add ERC20/ERC721 transfers from/to the given account
|
||||||
|
@ -341,7 +340,6 @@ func (d *ETHDownloader) subTransactionsFromTransactionData(tx *types.Transaction
|
||||||
case w_common.HopBridgeWithdrawalBondedEventType, w_common.HopBridgeTransferSentEventType:
|
case w_common.HopBridgeWithdrawalBondedEventType, w_common.HopBridgeTransferSentEventType:
|
||||||
mustAppend = true
|
mustAppend = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if mustAppend {
|
if mustAppend {
|
||||||
transfer := Transfer{
|
transfer := Transfer{
|
||||||
Type: w_common.EventTypeToSubtransactionType(eventType),
|
Type: w_common.EventTypeToSubtransactionType(eventType),
|
||||||
|
|
Loading…
Reference in New Issue