diff --git a/apps/indexer/lib/indexer/address_extraction.ex b/apps/indexer/lib/indexer/address_extraction.ex index 5943862e3b..060f4828ca 100644 --- a/apps/indexer/lib/indexer/address_extraction.ex +++ b/apps/indexer/lib/indexer/address_extraction.ex @@ -106,6 +106,16 @@ defmodule Indexer.AddressExtraction do %{from: :block_number, to: :fetched_coin_balance_block_number}, %{from: :token_contract_address_hash, to: :hash} ] + ], + mint_transfers: [ + [ + %{from: :block_number, to: :fetched_coin_balance_block_number}, + %{from: :from_address_hash, to: :hash} + ], + [ + %{from: :block_number, to: :fetched_coin_balance_block_number}, + %{from: :to_address_hash, to: :hash} + ] ] } @@ -362,6 +372,13 @@ defmodule Indexer.AddressExtraction do required(:token_contract_address_hash) => String.t(), required(:block_number) => non_neg_integer() } + ], + optional(:mint_transfers) => [ + %{ + required(:from_address_hash) => String.t(), + required(:to_address_hash) => String.t(), + required(:block_number) => non_neg_integer() + } ] }) :: [params] def extract_addresses(fetched_data, options \\ []) when is_map(fetched_data) and is_list(options) do diff --git a/apps/indexer/lib/indexer/block/fetcher.ex b/apps/indexer/lib/indexer/block/fetcher.ex index 79f9e6fca2..089eb1989a 100644 --- a/apps/indexer/lib/indexer/block/fetcher.ex +++ b/apps/indexer/lib/indexer/block/fetcher.ex @@ -6,7 +6,7 @@ defmodule Indexer.Block.Fetcher do require Logger alias Explorer.Chain.{Address, Block, Import} - alias Indexer.{CoinBalance, AddressExtraction, Token, TokenTransfers} + alias Indexer.{AddressExtraction, CoinBalance, MintTransfer, Token, TokenTransfers} alias Indexer.Address.{CoinBalances, TokenBalances} alias Indexer.Block.Fetcher.Receipts @@ -100,10 +100,12 @@ defmodule Indexer.Block.Fetcher do %{logs: logs, receipts: receipts} = receipt_params, transactions_with_receipts = Receipts.put(transactions_without_receipts, receipts), %{token_transfers: token_transfers, tokens: tokens} = TokenTransfers.parse(logs), + %{mint_transfers: mint_transfers} = MintTransfer.parse(logs), addresses = AddressExtraction.extract_addresses(%{ blocks: blocks, logs: logs, + mint_transfers: mint_transfers, token_transfers: token_transfers, transactions: transactions_with_receipts }), diff --git a/apps/indexer/lib/indexer/mint_transfer.ex b/apps/indexer/lib/indexer/mint_transfer.ex new file mode 100644 index 0000000000..352fedc007 --- /dev/null +++ b/apps/indexer/lib/indexer/mint_transfer.ex @@ -0,0 +1,63 @@ +defmodule Indexer.MintTransfer do + @moduledoc """ + Helper functions to parse addresses from mint transfers. + + When a network receives a mint coin, we can identify it using the `bridge_hash` in the first_topic. + Then we need to fetch the `from` and `to` address since there is no transaction or internal + transaction for it. Otherwise, those address may not be indexed. + """ + + @bridge_hash "0x3c798bbcf33115b42c728b8504cff11dd58736e9fa789f1cda2738db7d696b2a" + + @doc """ + Parses logs to find mint transfers. + + ## Examples + + iex> Indexer.MintTransfer.parse([ + ...> %{ + ...> address_hash: "0x867305d19606aadba405ce534e303d0e225f9556", + ...> block_number: 137_194, + ...> data: "0x0000000000000000000000000000000000000000000000001bc16d674ec80000", + ...> first_topic: "0x3c798bbcf33115b42c728b8504cff11dd58736e9fa789f1cda2738db7d696b2a", + ...> fourth_topic: nil, + ...> index: 1, + ...> second_topic: "0x0000000000000000000000009a4a90e2732f3fa4087b0bb4bf85c76d14833df1", + ...> third_topic: "0x0000000000000000000000007301cfa0e1756b71869e93d4e4dca5c7d0eb0aa6", + ...> transaction_hash: "0x1d5066d30ff3404a9306733136103ac2b0b989951c38df637f464f3667f8d4ee", + ...> type: "mined" + ...> } + ...> ]) + %{ + mint_transfers: [ + %{ + block_number: 137194, + from_address_hash: "0x7301cfa0e1756b71869e93d4e4dca5c7d0eb0aa6", + to_address_hash: "0x9a4a90e2732f3fa4087b0bb4bf85c76d14833df1" + } + ] + } + + """ + def parse(logs) do + addresses = + logs + |> Enum.filter(&(&1.first_topic == @bridge_hash)) + |> Enum.map(&parse_params/1) + + %{mint_transfers: addresses} + end + + defp parse_params(%{second_topic: second_topic, third_topic: third_topic, block_number: block_number}) + when not is_nil(second_topic) and not is_nil(third_topic) do + %{ + to_address_hash: truncate_address_hash(second_topic), + from_address_hash: truncate_address_hash(third_topic), + block_number: block_number + } + end + + defp truncate_address_hash("0x000000000000000000000000" <> truncated_hash) do + "0x#{truncated_hash}" + end +end diff --git a/apps/indexer/test/indexer/mint_transfer_test.exs b/apps/indexer/test/indexer/mint_transfer_test.exs new file mode 100644 index 0000000000..39ce61d0d8 --- /dev/null +++ b/apps/indexer/test/indexer/mint_transfer_test.exs @@ -0,0 +1,57 @@ +defmodule Indexer.MintTransferTest do + use ExUnit.Case, async: true + + alias Indexer.MintTransfer + + doctest Indexer.MintTransfer, import: true + + describe "parse/1" do + test "parses logs for fetch the mint transfer" do + logs = [ + %{ + address_hash: "0x867305d19606aadba405ce534e303d0e225f9556", + block_number: 137_194, + data: "0x0000000000000000000000000000000000000000000000001bc16d674ec80000", + first_topic: "0x3c798bbcf33115b42c728b8504cff11dd58736e9fa789f1cda2738db7d696b2a", + fourth_topic: nil, + index: 1, + second_topic: "0x0000000000000000000000009a4a90e2732f3fa4087b0bb4bf85c76d14833df1", + third_topic: "0x0000000000000000000000007301cfa0e1756b71869e93d4e4dca5c7d0eb0aa6", + transaction_hash: "0x1d5066d30ff3404a9306733136103ac2b0b989951c38df637f464f3667f8d4ee", + type: "mined" + } + ] + + expected = %{ + mint_transfers: [ + %{ + from_address_hash: "0x7301cfa0e1756b71869e93d4e4dca5c7d0eb0aa6", + to_address_hash: "0x9a4a90e2732f3fa4087b0bb4bf85c76d14833df1", + block_number: 137_194 + } + ] + } + + assert MintTransfer.parse(logs) == expected + end + end + + test "returns an empty list when the first topic isn't the brigde hash" do + logs = [ + %{ + address_hash: "0x867305d19606aadba405ce534e303d0e225f9556", + block_number: 137_194, + data: "0x0000000000000000000000000000000000000000000000001bc16d674ec80000", + first_topic: nil, + fourth_topic: nil, + index: 1, + second_topic: "0x0000000000000000000000009a4a90e2732f3fa4087b0bb4bf85c76d14833df1", + third_topic: "0x0000000000000000000000007301cfa0e1756b71869e93d4e4dca5c7d0eb0aa6", + transaction_hash: "0x1d5066d30ff3404a9306733136103ac2b0b989951c38df637f464f3667f8d4ee", + type: "mined" + } + ] + + assert MintTransfer.parse(logs) == %{mint_transfers: []} + end +end