feat: create unknown erc20

This commit is contained in:
Anthony Laibe 2023-08-31 09:47:24 +02:00
parent 555aae4d0f
commit 22fc83de59
5 changed files with 135 additions and 78 deletions

View File

@ -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)
}
}
}

View File

@ -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

View File

@ -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())
}
}

View File

@ -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)

View File

@ -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),