diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/overview/_details.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/overview/_details.html.eex
index 14e69303b2..a543d4f6e7 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/tokens/overview/_details.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/overview/_details.html.eex
@@ -42,6 +42,16 @@
+
+ <%= if @token.bridged do %>
+
+ <% end %>
<%= Address.checksum(@token.contract_address_hash) %>
diff --git a/apps/block_scout_web/lib/block_scout_web/views/tokens/overview_view.ex b/apps/block_scout_web/lib/block_scout_web/views/tokens/overview_view.ex
index eb82b2cc6f..59b8a71e40 100644
--- a/apps/block_scout_web/lib/block_scout_web/views/tokens/overview_view.ex
+++ b/apps/block_scout_web/lib/block_scout_web/views/tokens/overview_view.ex
@@ -56,4 +56,48 @@ defmodule BlockScoutWeb.Tokens.OverviewView do
price = token.usd_value
Decimal.mult(tokens, price)
end
+
+ def foreign_bridged_token_explorer_link(token) do
+ chain_id = Map.get(token, :foreign_chain_id)
+
+ base_token_explorer_link = get_base_token_explorer_link(chain_id)
+
+ foreign_token_contract_address_hash_string_no_prefix =
+ token.foreign_token_contract_address_hash.bytes
+ |> Base.encode16(case: :lower)
+
+ foreign_token_contract_address_hash_string = "0x" <> foreign_token_contract_address_hash_string_no_prefix
+
+ base_token_explorer_link <> foreign_token_contract_address_hash_string
+ end
+
+ defp get_base_token_explorer_link(chain_id) when not is_nil(chain_id) do
+ case Decimal.to_integer(chain_id) do
+ 100 ->
+ "https://blockscout.com/poa/xdai/tokens/"
+
+ 99 ->
+ "https://blockscout.com/poa/core/tokens/"
+
+ 77 ->
+ "https://blockscout.com/poa/sokol/tokens/"
+
+ 42 ->
+ "https://kovan.etherscan.io/token/"
+
+ 3 ->
+ "https://ropsten.etherscan.io/token/"
+
+ 4 ->
+ "https://rinkeby.etherscan.io/token/"
+
+ 5 ->
+ "https://goerli.etherscan.io/token/"
+
+ 1 ->
+ "https://etherscan.io/token/"
+ end
+ end
+
+ defp get_base_token_explorer_link(_), do: "https://etherscan.io/"
end
diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot
index 12ef88e1a0..342ad92dab 100644
--- a/apps/block_scout_web/priv/gettext/default.pot
+++ b/apps/block_scout_web/priv/gettext/default.pot
@@ -107,7 +107,7 @@ msgid "API for the %{subnetwork} - BlockScout"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:85
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:93
msgid "APIs"
msgstr ""
@@ -631,7 +631,7 @@ msgid "Error: Could not determine contract creator."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:99
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:107
msgid "Eth RPC"
msgstr ""
@@ -732,7 +732,7 @@ msgid "Github"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:89
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:97
msgid "GraphQL"
msgstr ""
@@ -1175,7 +1175,7 @@ msgstr ""
#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:51
#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:94
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:36
-#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:105
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:115
msgid "QR Code"
msgstr ""
@@ -1185,7 +1185,7 @@ msgid "Query"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:94
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:102
msgid "RPC"
msgstr ""
@@ -1243,14 +1243,14 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/index.html.eex:14
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:164
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:181
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:172
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:189
msgid "Search"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:158
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:162
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:166
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:170
msgid "Search by address, token symbol name, transaction hash, or block number"
msgstr ""
@@ -1446,7 +1446,7 @@ msgid "Topics"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:75
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:85
msgid "Total Supply"
msgstr ""
@@ -1601,7 +1601,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:16
#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:20
-#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:55
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:65
msgid "View Contract"
msgstr ""
@@ -1715,8 +1715,8 @@ msgstr ""
#: lib/block_scout_web/templates/address/overview.html.eex:142
#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:95
#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:103
-#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:106
-#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:114
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:116
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:124
msgid "Close"
msgstr ""
@@ -1733,7 +1733,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:69
-#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:63
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:73
msgid "Decimals"
msgstr ""
@@ -1850,6 +1850,8 @@ msgstr ""
#: lib/block_scout_web/templates/address/_tabs.html.eex:14
#: lib/block_scout_web/templates/address_token/index.html.eex:8
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:11
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:83
+#: lib/block_scout_web/templates/tokens/index.html.eex:4
#: lib/block_scout_web/views/address_view.ex:341
msgid "Tokens"
msgstr ""
@@ -1930,7 +1932,7 @@ msgid "Write Proxy"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:112
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:120
msgid "Apps"
msgstr ""
diff --git a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
index 12ef88e1a0..342ad92dab 100644
--- a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
+++ b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
@@ -107,7 +107,7 @@ msgid "API for the %{subnetwork} - BlockScout"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:85
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:93
msgid "APIs"
msgstr ""
@@ -631,7 +631,7 @@ msgid "Error: Could not determine contract creator."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:99
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:107
msgid "Eth RPC"
msgstr ""
@@ -732,7 +732,7 @@ msgid "Github"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:89
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:97
msgid "GraphQL"
msgstr ""
@@ -1175,7 +1175,7 @@ msgstr ""
#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:51
#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:94
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:36
-#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:105
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:115
msgid "QR Code"
msgstr ""
@@ -1185,7 +1185,7 @@ msgid "Query"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:94
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:102
msgid "RPC"
msgstr ""
@@ -1243,14 +1243,14 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/index.html.eex:14
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:164
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:181
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:172
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:189
msgid "Search"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:158
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:162
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:166
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:170
msgid "Search by address, token symbol name, transaction hash, or block number"
msgstr ""
@@ -1446,7 +1446,7 @@ msgid "Topics"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:75
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:85
msgid "Total Supply"
msgstr ""
@@ -1601,7 +1601,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:16
#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:20
-#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:55
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:65
msgid "View Contract"
msgstr ""
@@ -1715,8 +1715,8 @@ msgstr ""
#: lib/block_scout_web/templates/address/overview.html.eex:142
#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:95
#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:103
-#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:106
-#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:114
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:116
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:124
msgid "Close"
msgstr ""
@@ -1733,7 +1733,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/tokens/instance/overview/_details.html.eex:69
-#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:63
+#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:73
msgid "Decimals"
msgstr ""
@@ -1850,6 +1850,8 @@ msgstr ""
#: lib/block_scout_web/templates/address/_tabs.html.eex:14
#: lib/block_scout_web/templates/address_token/index.html.eex:8
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:11
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:83
+#: lib/block_scout_web/templates/tokens/index.html.eex:4
#: lib/block_scout_web/views/address_view.ex:341
msgid "Tokens"
msgstr ""
@@ -1930,7 +1932,7 @@ msgid "Write Proxy"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/layout/_topnav.html.eex:112
+#: lib/block_scout_web/templates/layout/_topnav.html.eex:120
msgid "Apps"
msgstr ""
diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/contract.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/contract.ex
index ed0e6d614f..0f1cec0cd4 100644
--- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/contract.ex
+++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/contract.ex
@@ -70,7 +70,7 @@ defmodule EthereumJSONRPC.Contract do
Enum.map(requests, fn _ -> format_error(error) end)
end
- defp eth_call_request(data, contract_address, id, block_number, from) do
+ def eth_call_request(data, contract_address, id, block_number, from) do
block =
case block_number do
nil -> "latest"
diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex
index 5ba04bea56..53db30525d 100644
--- a/apps/explorer/lib/explorer/chain.ex
+++ b/apps/explorer/lib/explorer/chain.ex
@@ -22,9 +22,9 @@ defmodule Explorer.Chain do
select: 3
]
- import EthereumJSONRPC, only: [integer_to_quantity: 1, fetch_block_internal_transactions: 2]
+ import EthereumJSONRPC, only: [integer_to_quantity: 1, json_rpc: 2, fetch_block_internal_transactions: 2]
- alias ABI.TypeDecoder
+ alias ABI.{TypeDecoder, TypeEncoder}
alias Ecto.Adapters.SQL
alias Ecto.{Changeset, Multi}
@@ -42,6 +42,7 @@ defmodule Explorer.Chain do
Address.CurrentTokenBalance,
Address.TokenBalance,
Block,
+ BridgedToken,
Data,
DecompiledSmartContract,
Hash,
@@ -1728,7 +1729,7 @@ defmodule Explorer.Chain do
base_query =
from(t in Token,
where: t.total_supply > ^0,
- order_by: [desc: t.holder_count],
+ order_by: [desc: t.holder_count, asc: t.name],
preload: [:contract_address]
)
@@ -3375,6 +3376,151 @@ defmodule Explorer.Chain do
Repo.stream_reduce(query, [], &[&1 | &2])
end
+ @doc """
+ Returns a list of token addresses `t:Address.t/0`s that don't have an
+ bridged property revealed.
+ """
+ def unprocessed_token_addresses_to_reveal_bridged_tokens do
+ query =
+ from(t in Token,
+ where: is_nil(t.bridged),
+ select: t.contract_address_hash
+ )
+
+ Repo.stream_reduce(query, [], &[&1 | &2])
+ end
+
+ @doc """
+ Fetches bridges status for tokens.
+ """
+ def fetch_tokens_bridged_status(token_addresses) do
+ Enum.each(token_addresses, fn token_address_hash ->
+ created_from_factory_query =
+ from(
+ it in InternalTransaction,
+ where: it.created_contract_address_hash == ^token_address_hash
+ )
+
+ created_from_factory =
+ created_from_factory_query
+ |> Repo.one()
+
+ if created_from_factory do
+ multi_token_bridge_mediator = Application.get_env(:block_scout_web, :multi_token_bridge_mediator)
+ %{transaction_hash: transaction_hash} = created_from_factory
+
+ if multi_token_bridge_mediator && multi_token_bridge_mediator !== "" do
+ {:ok, multi_token_bridge_mediator_hash} = Chain.string_to_address_hash(multi_token_bridge_mediator)
+
+ created_by_amb_mediator_query =
+ from(
+ it in InternalTransaction,
+ where: it.transaction_hash == ^transaction_hash,
+ where: it.to_address_hash == ^multi_token_bridge_mediator_hash
+ )
+
+ created_by_amb_mediator =
+ created_by_amb_mediator_query
+ |> Repo.all()
+
+ if Enum.count(created_by_amb_mediator) > 0 do
+ json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments)
+ # keccak 256 from getTokenInterfacesVersion()
+ get_token_interfaces_version_signature = "0x859ba28c"
+ # keccak 256 from foreignTokenAddress(address)
+ foreign_token_address_signature = "0x47ac7d6a"
+ # keccak 256 from bridgeContract()
+ bridge_contract_signature = "0xcd596583"
+ # keccak 256 from destinationChainId()
+ destination_chain_id_signature = "0xb0750611"
+
+ token_address_hash_abi_encoded =
+ [token_address_hash.bytes]
+ |> TypeEncoder.encode([:address])
+ |> Base.encode16()
+
+ foreign_token_address_method = foreign_token_address_signature <> token_address_hash_abi_encoded
+
+ with {:ok, _} <-
+ get_token_interfaces_version_signature
+ |> Contract.eth_call_request(token_address_hash, 1, nil, nil)
+ |> json_rpc(json_rpc_named_arguments),
+ {:ok, foreign_token_address_abi_encoded} <-
+ foreign_token_address_method
+ |> Contract.eth_call_request(
+ multi_token_bridge_mediator,
+ 1,
+ nil,
+ nil
+ )
+ |> json_rpc(json_rpc_named_arguments),
+ {:ok, bridge_contract} <-
+ bridge_contract_signature
+ |> Contract.eth_call_request(multi_token_bridge_mediator_hash, 1, nil, nil)
+ |> json_rpc(json_rpc_named_arguments) do
+ "0x" <> foreign_token_address_no_prefix = foreign_token_address_abi_encoded
+
+ <<_prefix::binary-size(24), foreign_token_address_hash_string_raw::binary()>> =
+ foreign_token_address_no_prefix
+
+ foreign_token_address_hash_string = "0x" <> foreign_token_address_hash_string_raw
+
+ {:ok, foreign_token_address_hash} = Chain.string_to_address_hash(foreign_token_address_hash_string)
+
+ "0x" <> bridge_contract_no_prefix = bridge_contract
+ <<_prefix::binary-size(24), multi_token_bridge_hash_string_raw::binary()>> = bridge_contract_no_prefix
+
+ multi_token_bridge_hash_string = "0x" <> multi_token_bridge_hash_string_raw
+
+ {:ok, foreign_chain_id_abi_encoded} =
+ destination_chain_id_signature
+ |> Contract.eth_call_request(multi_token_bridge_hash_string, 1, nil, nil)
+ |> json_rpc(json_rpc_named_arguments)
+
+ "0x" <> foreign_chain_id_abi_encoded_no_prefix = foreign_chain_id_abi_encoded
+ {foreign_chain_id, _} = Integer.parse(foreign_chain_id_abi_encoded_no_prefix)
+
+ set_bridged_token_metadata(token_address_hash, %{
+ foreign_chain_id: foreign_chain_id,
+ foreign_token_address_hash: foreign_token_address_hash
+ })
+
+ set_token_bridged_status(token_address_hash, true)
+ end
+ else
+ set_token_bridged_status(token_address_hash, false)
+ end
+ end
+ else
+ set_token_bridged_status(token_address_hash, false)
+ end
+ end)
+
+ :ok
+ end
+
+ defp set_token_bridged_status(token_address_hash, status) do
+ target_token = Repo.get!(Token, token_address_hash)
+ token = Changeset.change(target_token, bridged: status)
+
+ Repo.update(token)
+ end
+
+ defp set_bridged_token_metadata(token_address_hash, %{
+ foreign_chain_id: foreign_chain_id,
+ foreign_token_address_hash: foreign_token_address_hash
+ }) do
+ {:ok, _} =
+ Repo.insert(
+ %BridgedToken{
+ home_token_contract_address_hash: token_address_hash,
+ foreign_chain_id: foreign_chain_id,
+ foreign_token_contract_address_hash: foreign_token_address_hash
+ },
+ on_conflict: :nothing
+ )
+ end
+
@doc """
Fetches a `t:Token.t/0` by an address hash.
@@ -3394,8 +3540,11 @@ defmodule Explorer.Chain do
query =
from(
- token in Token,
- where: token.contract_address_hash == ^hash
+ t in Token,
+ left_join: bt in BridgedToken,
+ on: t.contract_address_hash == bt.home_token_contract_address_hash,
+ where: t.contract_address_hash == ^hash,
+ select: [t, bt]
)
query
@@ -3405,8 +3554,16 @@ defmodule Explorer.Chain do
nil ->
{:error, :not_found}
- %Token{} = token ->
- {:ok, token}
+ [%Token{} = token, %BridgedToken{} = bridged_token] ->
+ foreign_token_contract_address_hash = Map.get(bridged_token, :foreign_token_contract_address_hash)
+ foreign_chain_id = Map.get(bridged_token, :foreign_chain_id)
+
+ extended_token =
+ token
+ |> Map.put(:foreign_token_contract_address_hash, foreign_token_contract_address_hash)
+ |> Map.put(:foreign_chain_id, foreign_chain_id)
+
+ {:ok, extended_token}
end
end
diff --git a/apps/explorer/lib/explorer/chain/bridged_token.ex b/apps/explorer/lib/explorer/chain/bridged_token.ex
new file mode 100644
index 0000000000..15d13f4018
--- /dev/null
+++ b/apps/explorer/lib/explorer/chain/bridged_token.ex
@@ -0,0 +1,62 @@
+defmodule Explorer.Chain.BridgedToken do
+ @moduledoc """
+ Represents a bridged token.
+
+ """
+
+ use Explorer.Schema
+
+ import Ecto.Changeset
+
+ alias Explorer.Chain.{BridgedToken, Hash, Token}
+
+ @typedoc """
+ * `foreign_chain_id` - chain ID of a foreign token
+ * `foreign_token_contract_address_hash` - Foreign token's contract hash
+ * `home_token_contract_address` - The `t:Address.t/0` of the home token's contract
+ * `home_token_contract_address_hash` - Home token's contract hash foreign key
+ """
+ @type t :: %BridgedToken{
+ foreign_chain_id: Decimal.t(),
+ foreign_token_contract_address_hash: Hash.Address.t(),
+ home_token_contract_address: %Ecto.Association.NotLoaded{} | Address.t(),
+ home_token_contract_address_hash: Hash.Address.t()
+ }
+
+ @derive {Poison.Encoder,
+ except: [
+ :__meta__,
+ :home_token_contract_address,
+ :inserted_at,
+ :updated_at
+ ]}
+
+ @primary_key false
+ schema "bridged_tokens" do
+ field(:foreign_chain_id, :decimal)
+ field(:foreign_token_contract_address_hash, Hash.Address)
+
+ belongs_to(
+ :home_token_contract_address,
+ Token,
+ foreign_key: :home_token_contract_address_hash,
+ primary_key: true,
+ references: :contract_address_hash,
+ type: Hash.Address
+ )
+
+ timestamps()
+ end
+
+ @required_attrs ~w(home_token_contract_address_hash)a
+ @optional_attrs ~w(foreign_chain_id foreign_token_contract_address_hash)a
+
+ @doc false
+ def changeset(%BridgedToken{} = bridged_token, params \\ %{}) do
+ bridged_token
+ |> cast(params, @required_attrs ++ @optional_attrs)
+ |> validate_required(@required_attrs)
+ |> foreign_key_constraint(:home_token_contract_address)
+ |> unique_constraint(:home_token_contract_address_hash)
+ end
+end
diff --git a/apps/explorer/lib/explorer/chain/token.ex b/apps/explorer/lib/explorer/chain/token.ex
index 551563e4ad..b42b7e7124 100644
--- a/apps/explorer/lib/explorer/chain/token.ex
+++ b/apps/explorer/lib/explorer/chain/token.ex
@@ -45,7 +45,8 @@ defmodule Explorer.Chain.Token do
cataloged: boolean(),
contract_address: %Ecto.Association.NotLoaded{} | Address.t(),
contract_address_hash: Hash.Address.t(),
- holder_count: non_neg_integer() | nil
+ holder_count: non_neg_integer() | nil,
+ bridged: boolean()
}
@derive {Poison.Encoder,
@@ -65,6 +66,7 @@ defmodule Explorer.Chain.Token do
field(:type, :string)
field(:cataloged, :boolean)
field(:holder_count, :integer)
+ field(:bridged, :boolean)
belongs_to(
:contract_address,
diff --git a/apps/explorer/priv/repo/migrations/20200806125649_token_add_bridged_column.exs b/apps/explorer/priv/repo/migrations/20200806125649_token_add_bridged_column.exs
new file mode 100644
index 0000000000..ee059af16c
--- /dev/null
+++ b/apps/explorer/priv/repo/migrations/20200806125649_token_add_bridged_column.exs
@@ -0,0 +1,9 @@
+defmodule Explorer.Repo.Migrations.TokenAddBridgedColumn do
+ use Ecto.Migration
+
+ def change do
+ alter table(:tokens) do
+ add(:bridged, :boolean, null: true)
+ end
+ end
+end
diff --git a/apps/explorer/priv/repo/migrations/20200807064700_bridged_tokens_table.exs b/apps/explorer/priv/repo/migrations/20200807064700_bridged_tokens_table.exs
new file mode 100644
index 0000000000..a601379fcd
--- /dev/null
+++ b/apps/explorer/priv/repo/migrations/20200807064700_bridged_tokens_table.exs
@@ -0,0 +1,20 @@
+defmodule Explorer.Repo.Migrations.BridgedTokensTable do
+ use Ecto.Migration
+
+ def change do
+ create table(:bridged_tokens, primary_key: false) do
+ add(:foreign_chain_id, :numeric, null: false)
+ add(:foreign_token_contract_address_hash, :bytea, null: false)
+
+ add(
+ :home_token_contract_address_hash,
+ references(:tokens, column: :contract_address_hash, on_delete: :delete_all, type: :bytea),
+ null: false
+ )
+
+ timestamps()
+ end
+
+ create(unique_index(:bridged_tokens, :home_token_contract_address_hash))
+ end
+end
diff --git a/apps/indexer/config/config.exs b/apps/indexer/config/config.exs
index 24b9edd909..f24a08a0d2 100644
--- a/apps/indexer/config/config.exs
+++ b/apps/indexer/config/config.exs
@@ -44,6 +44,8 @@ config :indexer, Indexer.Fetcher.PendingTransaction.Supervisor,
# config :indexer, Indexer.Fetcher.ReplacedTransaction.Supervisor, disabled?: true
# config :indexer, Indexer.Fetcher.BlockReward.Supervisor, disabled?: true
config :indexer, Indexer.Fetcher.StakingPools.Supervisor, disabled?: true
+# System.get_env("MULTI_TOKEN_BRIDGE_MEDIATOR", "") == ""
+# config :indexer, Indexer.SetBridgedStatusForTokens.Supervisor, disabled?: true
config :indexer, Indexer.Supervisor, enabled: System.get_env("DISABLE_INDEXER") != "true"
diff --git a/apps/indexer/lib/indexer/set_bridged_status_for_tokens.ex b/apps/indexer/lib/indexer/set_bridged_status_for_tokens.ex
new file mode 100644
index 0000000000..1b62dff4fe
--- /dev/null
+++ b/apps/indexer/lib/indexer/set_bridged_status_for_tokens.ex
@@ -0,0 +1,62 @@
+defmodule Indexer.SetBridgedStatusForTokens do
+ @moduledoc """
+ Peiodically checks unprocessed tokens and sets bridged status.
+ """
+
+ use GenServer
+
+ require Logger
+
+ alias Explorer.Chain
+
+ @interval :timer.minutes(1)
+
+ # def child_spec([init_arguments]) do
+ # child_spec([init_arguments, []])
+ # end
+
+ # def child_spec([_init_arguments, _gen_server_options] = start_link_arguments) do
+ # default = %{
+ # id: __MODULE__,
+ # start: {__MODULE__, :start_link, start_link_arguments}
+ # }
+
+ # Supervisor.child_spec(default, [])
+ # end
+
+ def start_link([init_opts, gen_server_opts]) do
+ start_link(init_opts, gen_server_opts)
+ end
+
+ def start_link(init_opts, gen_server_opts) do
+ GenServer.start_link(__MODULE__, init_opts, gen_server_opts)
+ end
+
+ @impl GenServer
+ def init(opts) do
+ interval = opts[:interval] || @interval
+
+ Process.send_after(self(), :reveal_unprocessed_tokens, interval)
+
+ {:ok, %{interval: interval}}
+ end
+
+ @impl GenServer
+ def handle_info(:reveal_unprocessed_tokens, %{interval: interval} = state) do
+ Logger.debug(fn -> "Reveal unprocessed tokens" end)
+
+ {:ok, token_addresses} = Chain.unprocessed_token_addresses_to_reveal_bridged_tokens()
+
+ fetch_tokens_bridged_status(token_addresses)
+
+ Process.send_after(self(), :reveal_unprocessed_tokens, interval)
+
+ {:noreply, state}
+ end
+
+ defp fetch_tokens_bridged_status(token_addresses) do
+ :ok = Chain.fetch_tokens_bridged_status(token_addresses)
+
+ Logger.debug(fn -> "Bridged status fetched for tokens" end)
+ end
+end
diff --git a/apps/indexer/lib/indexer/supervisor.ex b/apps/indexer/lib/indexer/supervisor.ex
index f22b6de487..590771e149 100644
--- a/apps/indexer/lib/indexer/supervisor.ex
+++ b/apps/indexer/lib/indexer/supervisor.ex
@@ -5,7 +5,7 @@ defmodule Indexer.Supervisor do
use Supervisor
- alias Indexer.{Block, PendingOpsCleaner}
+ alias Indexer.{Block, PendingOpsCleaner, SetBridgedStatusForTokens}
alias Indexer.Block.{Catchup, Realtime}
alias Indexer.Fetcher.{
@@ -123,6 +123,7 @@ defmodule Indexer.Supervisor do
# Out-of-band fetchers
{CoinBalanceOnDemand.Supervisor, [json_rpc_named_arguments]},
+ {SetBridgedStatusForTokens, [[], []]},
# Temporary workers
{UncatalogedTokenTransfers.Supervisor, [[]]},