Refactor TransactionActions transformer

pull/6582/head
POA 2 years ago
parent b5c6af674a
commit 47c004f081
  1. 188
      apps/indexer/lib/indexer/transform/transaction_actions.ex

@ -12,7 +12,6 @@ defmodule Indexer.Transform.TransactionActions do
alias Explorer.Chain.{Hash, Token, TransactionActions} alias Explorer.Chain.{Hash, Token, TransactionActions}
alias Explorer.Repo alias Explorer.Repo
alias Explorer.SmartContract.Reader alias Explorer.SmartContract.Reader
alias Explorer.Token.MetadataRetriever
@mainnet 1 @mainnet 1
@optimism 10 @optimism 10
@ -58,6 +57,7 @@ defmodule Indexer.Transform.TransactionActions do
"type" => "function" "type" => "function"
} }
] ]
@erc20_abi [%{"constant"=>true,"inputs"=>[],"name"=>"symbol","outputs"=>[%{"name"=>"","type"=>"string"}],"payable"=>false,"stateMutability"=>"view","type"=>"function"},%{"constant"=>true,"inputs"=>[],"name"=>"decimals","outputs"=>[%{"name"=>"","type"=>"uint8"}],"payable"=>false,"stateMutability"=>"view","type"=>"function"}]
@doc """ @doc """
Returns a list of transaction actions given a list of logs. Returns a list of transaction actions given a list of logs.
@ -77,7 +77,7 @@ defmodule Indexer.Transform.TransactionActions do
logs logs
|> uniswap_filter_logs() |> uniswap_filter_logs()
|> logs_group_by_txs() |> logs_group_by_txs()
|> uniswap(actions) |> uniswap(actions, chain_id)
else else
actions actions
end end
@ -85,7 +85,7 @@ defmodule Indexer.Transform.TransactionActions do
%{transaction_actions: actions} %{transaction_actions: actions}
end end
defp uniswap(logs_grouped, actions) do defp uniswap(logs_grouped, actions, chain_id) do
# create a list of UniswapV3Pool legitimate contracts # create a list of UniswapV3Pool legitimate contracts
legitimate = uniswap_legitimate_pools(logs_grouped) legitimate = uniswap_legitimate_pools(logs_grouped)
@ -126,9 +126,8 @@ defmodule Indexer.Transform.TransactionActions do
end) end)
actions_acc = actions_acc =
actions_acc ++ Enum.reduce(mint_nft_ids, actions_acc, fn {to, ids}, acc ->
Enum.map(mint_nft_ids, fn {to, ids} -> acc ++ [%{
%{
hash: tx_hash, hash: tx_hash,
protocol: "uniswap_v3", protocol: "uniswap_v3",
data: %{ data: %{
@ -139,13 +138,11 @@ defmodule Indexer.Transform.TransactionActions do
ids: ids ids: ids
}, },
type: "mint_nft" type: "mint_nft"
} }]
end) end)
# go through other actions # go through other actions
actions_acc = Enum.reduce(tx_logs, actions_acc, fn log, acc ->
actions_acc ++
Enum.reduce(tx_logs, [], fn log, acc ->
first_topic = String.downcase(log.first_topic) first_topic = String.downcase(log.first_topic)
if first_topic == "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" do if first_topic == "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" do
@ -155,69 +152,53 @@ defmodule Indexer.Transform.TransactionActions do
pool_address = String.downcase(log.address_hash) pool_address = String.downcase(log.address_hash)
if Enum.count(legitimate[pool_address]) == 0 do if Enum.count(legitimate[pool_address]) == 0 do
# this is not legitimate uniswap pool, so skip this address # this is not legitimate uniswap pool, so skip this event
acc acc
else else
[token0_address, token1_address] = legitimate[pool_address] token_address = legitimate[pool_address]
# try to read token symbols and decimals from cache # try to read token symbols and decimals from the cache
token0_data = get_token_data_from_cache(token0_address) token_data = Enum.map(token_address, &get_token_data_from_cache/1)
token1_data = get_token_data_from_cache(token1_address)
select_tokens_from_db = []
# a list of token addresses which we should select from the database
select_tokens_from_db = select_tokens_from_db =
select_tokens_from_db ++ token_data
if is_nil(token0_data.symbol) or is_nil(token0_data.decimals) do |> Enum.with_index()
[token0_address] |> Enum.reduce([], fn {data, i}, select ->
if is_nil(data.symbol) or is_nil(data.decimals) do
select ++ [Enum.at(token_address, i)]
else else
[] select
end end
end)
select_tokens_from_db = token_data =
select_tokens_from_db ++ if Enum.count(select_tokens_from_db) == 0 do
if is_nil(token1_data.symbol) or is_nil(token1_data.decimals) do token_data
[token1_address]
else else
[] # try to read token symbols and decimals from the database and then save to the cache
end
{token0_data, token1_data} =
if Enum.count(select_tokens_from_db) != 0 do
# try to read token symbols and decimals from DB and save to cache
query = from( query = from(
t in Token, t in Token,
where: t.contract_address_hash in ^select_tokens_from_db, where: t.contract_address_hash in ^select_tokens_from_db,
select: {t.symbol, t.decimals, t.contract_address_hash} select: {t.symbol, t.decimals, t.contract_address_hash}
) )
tokens_data =
query query
|> Repo.all() |> Repo.all()
|> Enum.reduce(%{token0_symbol: token0_data.symbol, token0_decimals: token0_data.decimals, token1_symbol: token1_data.symbol, token1_decimals: token1_data.decimals}, fn {symbol, decimals, contract_address_hash}, query_acc -> |> Enum.reduce(token_data, fn {symbol, decimals, contract_address_hash}, token_data_acc ->
if String.downcase(Hash.to_string(contract_address_hash)) == token0_address do contract_address_hash = String.downcase(Hash.to_string(contract_address_hash))
symbol =
if is_nil(symbol) or symbol == "" do
# if db field is empty, take it from the cache
query_acc.token0_symbol
else
symbol
end
decimals = index =
if is_nil(decimals) do if contract_address_hash == Enum.at(token_address, 0) do
# if db field is empty, take it from the cache 0
query_acc.token0_decimals
else else
decimals 1
end end
%{query_acc | token0_symbol: symbol, token0_decimals: decimals}
else
symbol = symbol =
if is_nil(symbol) or symbol == "" do if is_nil(symbol) or symbol == "" do
# if db field is empty, take it from the cache # if db field is empty, take it from the cache
query_acc.token1_symbol Enum.at(token_data_acc, index).symbol
else else
symbol symbol
end end
@ -225,63 +206,112 @@ defmodule Indexer.Transform.TransactionActions do
decimals = decimals =
if is_nil(decimals) do if is_nil(decimals) do
# if db field is empty, take it from the cache # if db field is empty, take it from the cache
query_acc.token1_decimals Enum.at(token_data_acc, index).decimals
else else
decimals decimals
end end
%{query_acc | token1_symbol: symbol, token1_decimals: decimals} new_data = %{symbol: symbol, decimals: decimals}
end
end)
token0_data = %{symbol: tokens_data.token0_symbol, decimals: tokens_data.token0_decimals} :ets.insert(:tokens_data_cache, {contract_address_hash, new_data})
token1_data = %{symbol: tokens_data.token1_symbol, decimals: tokens_data.token1_decimals}
:ets.insert(:tokens_data_cache, {token0_address, token0_data}) List.replace_at(token_data_acc, index, new_data)
:ets.insert(:tokens_data_cache, {token1_address, token1_data}) end)
end
{token0_data, token1_data} # if tokens are not in the cache, nor in the DB, read them through RPC
read_tokens_from_rpc =
token_data
|> Enum.with_index()
|> Enum.reduce([], fn {data, i}, read ->
if is_nil(data.symbol) or data.symbol == "" or is_nil(data.decimals) do
read ++ [Enum.at(token_address, i)]
else else
{token0_data, token1_data} read
end end
end)
read_tokens_from_rpc = [] token_data =
if Enum.count(read_tokens_from_rpc) == 0 do
token_data
else
# try to read token symbols and decimals from RPC and then save to the cache
requests =
read_tokens_from_rpc
|> Enum.map(fn token_contract_address ->
Enum.map(["95d89b41", "313ce567"], fn method_id ->
%{
contract_address: token_contract_address,
method_id: method_id,
args: []
}
end)
end)
|> List.flatten()
read_tokens_from_rpc = max_retries = Application.get_env(:explorer, :token_functions_reader_max_retries)
read_tokens_from_rpc ++ responses = read_contracts_with_retries(requests, @erc20_abi, max_retries)
if is_nil(token0_data.symbol) or token0_data.symbol == "" or is_nil(token0_data.decimals) do
[token0_address] if Enum.count(requests) != Enum.count(responses) do
Logger.error(fn -> "Cannot read symbol and decimals of an ERC-20 token contract" end)
token_data
else else
[] Enum.zip(requests, responses)
|> Enum.reduce(token_data, fn {request, {_status, response} = _resp}, token_data_acc ->
response =
case response do
[item] -> item
items -> items
end end
read_tokens_from_rpc = index =
read_tokens_from_rpc ++ if request.contract_address == Enum.at(token_address, 0) do
if is_nil(token1_data.symbol) or token1_data.symbol == "" or is_nil(token1_data.decimals) do 0
[token1_address]
else else
[] 1
end end
{token0_data, token1_data} = data = Enum.at(token_data_acc, index)
if Enum.count(read_tokens_from_rpc) != 0 do
# try to read token symbols and decimals from RPC and save to cache new_data =
# todo if atomized_key(request.method_id) == :symbol do
%{data | symbol: response}
else else
{token0_data, token1_data} %{data | decimals: response}
end
:ets.insert(:tokens_data_cache, {request.contract_address, new_data})
List.replace_at(token_data_acc, index, new_data)
end)
end
end end
if Enum.any?(token_data, fn token -> is_nil(token.symbol) or token.symbol == "" or is_nil(token.decimals) end) do
# token data is not available, so skip this event
acc
else
token0_symbol = uniswap_clarify_token_symbol(Enum.at(token_data, 0).symbol, chain_id)
token1_symbol = uniswap_clarify_token_symbol(Enum.at(token_data, 1).symbol, chain_id)
# todo # todo
acc acc
end end
end end
end
end) end)
actions_acc
end) end)
end end
defp uniswap_clarify_token_symbol(symbol, chain_id) do
if symbol == "WETH" && Enum.member?([@mainnet, @optimism], chain_id) do
"Ether"
else
symbol
end
end
defp uniswap_filter_logs(logs) do defp uniswap_filter_logs(logs) do
logs logs
|> Enum.filter(fn log -> |> Enum.filter(fn log ->
@ -391,10 +421,14 @@ defmodule Indexer.Transform.TransactionActions do
defp atomized_key("token1"), do: :token1 defp atomized_key("token1"), do: :token1
defp atomized_key("fee"), do: :fee defp atomized_key("fee"), do: :fee
defp atomized_key("getPool"), do: :getPool defp atomized_key("getPool"), do: :getPool
defp atomized_key("symbol"), do: :symbol
defp atomized_key("decimals"), do: :decimals
defp atomized_key("0dfe1681"), do: :token0 defp atomized_key("0dfe1681"), do: :token0
defp atomized_key("d21220a7"), do: :token1 defp atomized_key("d21220a7"), do: :token1
defp atomized_key("ddca3f43"), do: :fee defp atomized_key("ddca3f43"), do: :fee
defp atomized_key("1698ee82"), do: :getPool defp atomized_key("1698ee82"), do: :getPool
defp atomized_key("95d89b41"), do: :symbol
defp atomized_key("313ce567"), do: :decimals
defp clear_actions(logs_grouped) do defp clear_actions(logs_grouped) do
logs_grouped logs_grouped

Loading…
Cancel
Save