feat: create unknown erc20
This commit is contained in:
parent
555aae4d0f
commit
22fc83de59
|
@ -90,6 +90,22 @@ type Token struct {
|
|||
BuiltOn string `json:"builtOn"`
|
||||
MarketValuesPerCurrency map[string]TokenMarketValues `json:"marketValuesPerCurrency"`
|
||||
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 {
|
||||
|
@ -184,8 +200,8 @@ func (r *Reader) GetWalletToken(ctx context.Context, addresses []common.Address)
|
|||
}
|
||||
currencies = append(currencies, currency)
|
||||
currencies = append(currencies, getFixedCurrencies()...)
|
||||
|
||||
allTokens, err := r.tokenManager.GetTokensByChainIDs(chainIDs)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -251,68 +267,73 @@ func (r *Reader) GetWalletToken(ctx context.Context, addresses []common.Address)
|
|||
}
|
||||
err = group.Error()
|
||||
result := make(map[common.Address][]Token)
|
||||
verifiedTokens, unverifiedTokens := splitVerifiedTokens(allTokens)
|
||||
|
||||
for _, address := range addresses {
|
||||
for symbol, tokens := range getTokenBySymbols(allTokens) {
|
||||
balancesPerChain := make(map[uint64]ChainBalance)
|
||||
decimals := tokens[0].Decimals
|
||||
anyPositiveBalance := false
|
||||
for _, token := range tokens {
|
||||
hexBalance := balances[token.ChainID][address][token.Address]
|
||||
balance := big.NewFloat(0.0)
|
||||
if hexBalance != nil {
|
||||
balance = new(big.Float).Quo(
|
||||
new(big.Float).SetInt(hexBalance.ToInt()),
|
||||
big.NewFloat(math.Pow(10, float64(decimals))),
|
||||
)
|
||||
for _, tokenList := range [][]*token.Token{verifiedTokens, unverifiedTokens} {
|
||||
for symbol, tokens := range getTokenBySymbols(tokenList) {
|
||||
balancesPerChain := make(map[uint64]ChainBalance)
|
||||
decimals := tokens[0].Decimals
|
||||
anyPositiveBalance := false
|
||||
for _, token := range tokens {
|
||||
hexBalance := balances[token.ChainID][address][token.Address]
|
||||
balance := big.NewFloat(0.0)
|
||||
if hexBalance != nil {
|
||||
balance = new(big.Float).Quo(
|
||||
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) {
|
||||
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,
|
||||
if !anyPositiveBalance && !belongsToMandatoryTokens(symbol) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
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),
|
||||
}
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
// pegged, while "USD" means it's pegged to the United States Dollar.
|
||||
PegSymbol string `json:"pegSymbol"`
|
||||
|
||||
Verified bool `json:"verified"`
|
||||
}
|
||||
|
||||
func (t *Token) IsNative() bool {
|
||||
|
@ -54,6 +56,7 @@ type ManagerInterface interface {
|
|||
type Manager struct {
|
||||
db *sql.DB
|
||||
RPCClient *rpc.Client
|
||||
contractMaker *contracts.ContractMaker
|
||||
networkManager *network.Manager
|
||||
stores []store
|
||||
tokenList []*Token
|
||||
|
@ -66,9 +69,18 @@ func NewTokenManager(
|
|||
RPCClient *rpc.Client,
|
||||
networkManager *network.Manager,
|
||||
) *Manager {
|
||||
maker, _ := contracts.NewContractMaker(RPCClient)
|
||||
// Order of stores is important when merging token lists. The former prevale
|
||||
tokenManager := &Manager{db, RPCClient, networkManager, []store{newUniswapStore(), newDefaultStore()}, nil, nil, false}
|
||||
return tokenManager
|
||||
return &Manager{
|
||||
db,
|
||||
RPCClient,
|
||||
maker,
|
||||
networkManager,
|
||||
[]store{newUniswapStore(), newDefaultStore()},
|
||||
nil,
|
||||
nil,
|
||||
false,
|
||||
}
|
||||
}
|
||||
|
||||
// overrideTokensInPlace overrides tokens in the store with the ones from the networks
|
||||
|
@ -135,6 +147,8 @@ func (tm *Manager) fetchTokens() {
|
|||
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)
|
||||
|
@ -224,9 +238,31 @@ func (tm *Manager) FindTokenByAddress(chainID uint64, address common.Address) *T
|
|||
return token
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
tokens, err := tm.GetTokens(chainID)
|
||||
if err != nil {
|
||||
|
@ -248,7 +284,7 @@ func (tm *Manager) GetAllTokensAndNativeCurrencies() ([]*Token, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
networks, err := tm.RPCClient.NetworkManager.Get(false)
|
||||
networks, err := tm.networkManager.Get(false)
|
||||
if err != nil {
|
||||
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) {
|
||||
backend, err := tm.RPCClient.EthClient(chainID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
caller, err := ierc20.NewIERC20Caller(address, backend)
|
||||
caller, err := tm.contractMaker.NewERC20(chainID, address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -349,6 +381,7 @@ func (tm *Manager) DiscoverToken(ctx context.Context, chainID uint64, address co
|
|||
Name: name,
|
||||
Symbol: symbol,
|
||||
Decimals: uint(decimal),
|
||||
ChainID: chainID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -432,6 +465,7 @@ func (tm *Manager) ToToken(network *params.Network) *Token {
|
|||
Symbol: network.NativeCurrencySymbol,
|
||||
Decimals: uint(network.NativeCurrencyDecimals),
|
||||
ChainID: network.ChainID,
|
||||
Verified: true,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -573,14 +607,11 @@ func (tm *Manager) GetBalances(parent context.Context, clients map[uint64]*chain
|
|||
response[account][token] = &sumHex
|
||||
mu.Unlock()
|
||||
}
|
||||
contractMaker, err := contracts.NewContractMaker(tm.RPCClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for clientIdx := range clients {
|
||||
client := clients[clientIdx]
|
||||
|
||||
ethScanContract, err := contractMaker.NewEthScan(client.ChainID)
|
||||
ethScanContract, err := tm.contractMaker.NewEthScan(client.ChainID)
|
||||
|
||||
if err == nil {
|
||||
fetchChainBalance := false
|
||||
|
@ -711,13 +742,9 @@ func (tm *Manager) GetBalancesByChain(parent context.Context, clients map[uint64
|
|||
mu.Unlock()
|
||||
}
|
||||
|
||||
contractMaker, err := contracts.NewContractMaker(tm.RPCClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for clientIdx := range clients {
|
||||
client := clients[clientIdx]
|
||||
ethScanContract, err := contractMaker.NewEthScan(client.ChainID)
|
||||
ethScanContract, err := tm.contractMaker.NewEthScan(client.ChainID)
|
||||
if err != nil {
|
||||
log.Error("error scanning contract", "err", err)
|
||||
return nil, err
|
||||
|
|
|
@ -16,7 +16,7 @@ import (
|
|||
func setupTestTokenDB(t *testing.T) (*Manager, func()) {
|
||||
db, err := helpers.SetupTestMemorySQLDB(walletdatabase.DbInitializer{})
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -404,6 +404,8 @@ func (c *transfersCommand) Run(ctx context.Context) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
c.processUnknownErc20CommunityTransactions(ctx, allTransfers)
|
||||
|
||||
err = c.processMultiTransactions(ctx, allTransfers)
|
||||
if err != nil {
|
||||
log.Error("processMultiTransactions error", "error", err)
|
||||
|
@ -548,6 +550,15 @@ func (c *transfersCommand) checkAndProcessBridgeMultiTx(ctx context.Context, tx
|
|||
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 {
|
||||
txByTxHash := subTransactionListToTransactionsByTxHash(allTransfers)
|
||||
|
||||
|
|
|
@ -317,7 +317,6 @@ func (d *ETHDownloader) subTransactionsFromTransactionData(tx *types.Transaction
|
|||
}
|
||||
|
||||
rst := make([]Transfer, 0, len(receipt.Logs))
|
||||
|
||||
for _, log := range receipt.Logs {
|
||||
eventType := w_common.GetEventType(log)
|
||||
// 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:
|
||||
mustAppend = true
|
||||
}
|
||||
|
||||
if mustAppend {
|
||||
transfer := Transfer{
|
||||
Type: w_common.EventTypeToSubtransactionType(eventType),
|
||||
|
|
Loading…
Reference in New Issue