feat: precompiled contracts ABI import (#9899)

* Initial implementation of precompiled contracts ABI import

* Documention added

* address Sobelow finding

* another attempt address Sobelow finding

* Apply evident suggestions from code review

Co-authored-by: Kirill Fedoseev <kirill@blockscout.com>

* Fix afer merge

* Small inconsistency in spec

* different path for different mix environment

* fix for formatting issue

---------

Co-authored-by: Kirill Fedoseev <kirill@blockscout.com>
pull/9977/head
Alexander Kolotov 7 months ago committed by GitHub
parent 30d0bb5668
commit 6f5dc3b5d3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 89
      apps/explorer/lib/explorer/chain/smart_contract.ex
  2. 239
      apps/explorer/lib/explorer/chain_spec/genesis_data.ex
  3. 60
      apps/explorer/lib/explorer/chain_spec/geth/importer.ex
  4. 30
      apps/explorer/lib/explorer/smart_contract/solidity/publisher.ex
  5. 130
      config/assets/precompiles-arbitrum.json
  6. 15
      config/runtime.exs
  7. 1
      docker/Dockerfile

@ -861,24 +861,39 @@ defmodule Explorer.Chain.SmartContract do
end
@doc """
Inserts a `t:SmartContract.t/0`.
Inserts a new smart contract and associated data into the database.
As part of inserting a new smart contract, an additional record is inserted for
naming the address for reference.
This function creates a new smart contract entry in the database. It calculates an MD5 hash of
the contract's bytecode, upserts contract methods, and handles the linkage of external libraries and
additional secondary sources. It also updates the associated address to mark the contract as
verified and manages the naming records for the address.
## Parameters
- `attrs`: Attributes for the new smart contract.
- `external_libraries`: A list of external libraries used by the contract.
- `secondary_sources`: Additional source data related to the contract.
## Returns
- `{:ok, smart_contract}` on successful insertion.
- `{:error, data}` on failure, returning the changeset or, if any issues happen during setting the address as verified, an error message.
"""
@spec create_smart_contract(map(), list(), list()) :: {:ok, __MODULE__.t()} | {:error, Ecto.Changeset.t()}
def create_smart_contract(attrs \\ %{}, external_libraries \\ [], secondary_sources \\ []) do
new_contract = %__MODULE__{}
# Updates contract attributes with calculated MD5 for the contract's bytecode
attrs =
attrs
|> Helper.add_contract_code_md5()
# Prepares changeset and extends it with external libraries.
# As part of changeset preparation and verification, contract methods are upserted
smart_contract_changeset =
new_contract
|> __MODULE__.changeset(attrs)
|> Changeset.put_change(:external_libraries, external_libraries)
# Prepares changesets for additional sources associated with the contract
new_contract_additional_source = %SmartContractAdditionalSource{}
smart_contract_additional_sources_changesets =
@ -894,7 +909,10 @@ defmodule Explorer.Chain.SmartContract do
address_hash = Changeset.get_field(smart_contract_changeset, :address_hash)
# Enforce ShareLocks tables order (see docs: sharelocks.md)
# Prepares the queries to update Explorer.Chain.Address to mark the contract as
# verified, clear the primary flag for the contract address in
# Explorer.Chain.Address.Name if any (enforce ShareLocks tables order (see
# docs: sharelocks.md)) and insert the contract details.
insert_contract_query =
Multi.new()
|> Multi.run(:set_address_verified, fn repo, _ -> set_address_verified(repo, address_hash) end)
@ -903,6 +921,8 @@ defmodule Explorer.Chain.SmartContract do
end)
|> Multi.insert(:smart_contract, smart_contract_changeset)
# Updates the queries from the previous step with inserting additional sources
# of the contract
insert_contract_query_with_additional_sources =
smart_contract_additional_sources_changesets
|> Enum.with_index()
@ -910,10 +930,12 @@ defmodule Explorer.Chain.SmartContract do
Multi.insert(multi, "smart_contract_additional_source_#{Integer.to_string(index)}", changeset)
end)
# Applying the queries to the database
insert_result =
insert_contract_query_with_additional_sources
|> Repo.transaction()
# Set the primary mark for the contract name
AddressName.create_primary_address_name(Repo, Changeset.get_field(smart_contract_changeset, :name), address_hash)
case insert_result do
@ -929,16 +951,28 @@ defmodule Explorer.Chain.SmartContract do
end
@doc """
Updates a `t:SmartContract.t/0`.
Has the similar logic as create_smart_contract/1.
Used in cases when you need to update row in DB contains SmartContract, e.g. in case of changing
status `partially verified` to `fully verified` (re-verify).
Updates an existing smart contract and associated data into the database.
This function is similar to `create_smart_contract/1` but is used for updating an existing smart
contract, such as changing its verification status from `partially verified` to `fully verified`.
It handles the updates including external libraries and secondary sources associated with the contract.
Notably, it updates contract methods based on the new ABI provided: if the new ABI does not contain
some of the previously listed methods, those methods are retained in the database.
## Parameters
- `attrs`: Attributes for the smart contract to be updated.
- `external_libraries`: A list of external libraries associated with the contract.
- `secondary_sources`: A list of secondary source data associated with the contract.
## Returns
- `{:ok, smart_contract}` on successful update.
- `{:error, changeset}` on failure, indicating issues with the data provided for update.
"""
@spec update_smart_contract(map(), list(), list()) :: {:ok, __MODULE__.t()} | {:error, Ecto.Changeset.t()}
def update_smart_contract(attrs \\ %{}, external_libraries \\ [], secondary_sources \\ []) do
address_hash = Map.get(attrs, :address_hash)
# Removes all additional sources associated with the contract
query_sources =
from(
source in SmartContractAdditionalSource,
@ -947,14 +981,20 @@ defmodule Explorer.Chain.SmartContract do
_delete_sources = Repo.delete_all(query_sources)
# Retrieve the existing smart contract
query = get_smart_contract_query(address_hash)
smart_contract = Repo.one(query)
# Updates existing changeset and extends it with external libraries.
# As part of changeset preparation and verification, contract methods are
# updated as so if new ABI does not contain some of previous methods, they
# are still kept in the database
smart_contract_changeset =
smart_contract
|> __MODULE__.changeset(attrs)
|> Changeset.put_change(:external_libraries, external_libraries)
# Prepares changesets for additional sources associated with the contract
new_contract_additional_source = %SmartContractAdditionalSource{}
smart_contract_additional_sources_changesets =
@ -968,7 +1008,9 @@ defmodule Explorer.Chain.SmartContract do
[]
end
# Enforce ShareLocks tables order (see docs: sharelocks.md)
# Prepares the queries to clear the primary flag for the contract address in
# Explorer.Chain.Address.Name if any (enforce ShareLocks tables order (see
# docs: sharelocks.md)) and updated the contract details.
insert_contract_query =
Multi.new()
|> Multi.run(:clear_primary_address_names, fn repo, _ ->
@ -976,6 +1018,8 @@ defmodule Explorer.Chain.SmartContract do
end)
|> Multi.update(:smart_contract, smart_contract_changeset)
# Updates the queries from the previous step with inserting additional sources
# of the contract
insert_contract_query_with_additional_sources =
smart_contract_additional_sources_changesets
|> Enum.with_index()
@ -983,10 +1027,12 @@ defmodule Explorer.Chain.SmartContract do
Multi.insert(multi, "smart_contract_additional_source_#{Integer.to_string(index)}", changeset)
end)
# Applying the queries to the database
insert_result =
insert_contract_query_with_additional_sources
|> Repo.transaction()
# Set the primary mark for the contract name
AddressName.create_primary_address_name(Repo, Changeset.get_field(smart_contract_changeset, :name), address_hash)
case insert_result do
@ -995,9 +1041,6 @@ defmodule Explorer.Chain.SmartContract do
{:error, :smart_contract, changeset, _} ->
{:error, changeset}
{:error, :set_address_verified, message, _} ->
{:error, message}
end
end
@ -1059,10 +1102,14 @@ defmodule Explorer.Chain.SmartContract do
end
@doc """
Checks if it exists a verified `t:Explorer.Chain.SmartContract.t/0` for the
`t:Explorer.Chain.Address.t/0` with the provided `hash`.
Checks if a `Explorer.Chain.SmartContract` exists for the provided address hash.
Returns `true` if found and `false` otherwise.
## Parameters
- `address_hash_str` or `address_hash`: The hash of the address in binary string
form or directly as an address hash.
## Returns
- `boolean()`: `true` if a smart contract exists, `false` otherwise.
"""
@spec verified?(Hash.Address.t() | String.t()) :: boolean()
def verified?(address_hash_str) when is_binary(address_hash_str) do
@ -1118,7 +1165,13 @@ defmodule Explorer.Chain.SmartContract do
end
@doc """
Gets smart-contract by address hash
Composes a query for fetching a smart contract by its address hash.
## Parameters
- `address_hash`: The hash of the smart contract's address.
## Returns
- An `Ecto.Query.t()` that represents the query to fetch the smart contract.
"""
@spec get_smart_contract_query(Hash.Address.t() | binary) :: Ecto.Query.t()
def get_smart_contract_query(address_hash) do
@ -1263,6 +1316,8 @@ defmodule Explorer.Chain.SmartContract do
end
end
# Checks if a smart contract exists in `Explorer.Chain.SmartContract` for a given
# address hash.
@spec verified_smart_contract_exists?(Hash.Address.t()) :: boolean()
defp verified_smart_contract_exists?(address_hash) do
query = get_smart_contract_query(address_hash)

@ -1,6 +1,10 @@
defmodule Explorer.ChainSpec.GenesisData do
@moduledoc """
Fetches genesis data.
Handles the genesis data import.
This module is responsible for managing the import of genesis data into the
database, which includes pre-mined balances and precompiled smart contract
bytecodes.
"""
use GenServer
@ -10,6 +14,7 @@ defmodule Explorer.ChainSpec.GenesisData do
alias Explorer.ChainSpec.Geth.Importer, as: GethImporter
alias Explorer.ChainSpec.Parity.Importer
alias Explorer.Helper
alias Explorer.SmartContract.Solidity.Publisher, as: SolidityPublisher
alias HTTPoison.Response
@interval :timer.minutes(2)
@ -28,13 +33,23 @@ defmodule Explorer.ChainSpec.GenesisData do
# Callback for errored fetch
@impl GenServer
def handle_info({_ref, {:error, reason}}, state) do
Logger.warn(fn -> "Failed to fetch genesis data '#{reason}'." end)
Logger.warn(fn -> "Failed to fetch and import genesis data or precompiled contracts: '#{reason}'." end)
fetch_genesis_data()
{:noreply, state}
end
# Initiates the import of genesis data.
#
# This function triggers the fetching and importing of genesis data, including pre-mined balances and precompiled smart contract bytecodes.
#
# ## Parameters
# - `:import`: The message that triggers this function.
# - `state`: The current state of the GenServer.
#
# ## Returns
# - `{:noreply, state}`
@impl GenServer
def handle_info(:import, state) do
Logger.debug(fn -> "Importing genesis data" end)
@ -57,39 +72,87 @@ defmodule Explorer.ChainSpec.GenesisData do
end
@doc """
Fetches pre-mined balances and pre-compiled smart-contract bytecodes from genesis.json
Fetches and processes the genesis data, which includes pre-mined balances and precompiled smart contract bytecodes.
This function retrieves the chain specification and precompiled contracts
configuration from specified paths in the application settings. Then it
asynchronously extends the chain spec with precompiled contracts, imports
genesis accounts, and the precompiled contracts' sources and ABIs.
## Returns
- `Task.t()`: A task handle if the fetch and processing are scheduled successfully.
- `:ok`: Indicates no fetch was attempted due to missing configuration paths.
"""
@spec fetch_genesis_data() :: Task.t() | :ok
def fetch_genesis_data do
path = Application.get_env(:explorer, __MODULE__)[:chain_spec_path]
chain_spec_path = get_path(:chain_spec_path)
precompiled_config_path = get_path(:precompiled_config_path)
if path do
if is_nil(chain_spec_path) and is_nil(precompiled_config_path) do
Logger.warn(fn -> "Genesis data is not fetched. Neither chain spec path or precompiles config path are set." end)
else
json_rpc_named_arguments = Application.fetch_env!(:indexer, :json_rpc_named_arguments)
variant = Keyword.fetch!(json_rpc_named_arguments, :variant)
Task.Supervisor.async_nolink(Explorer.GenesisDataTaskSupervisor, fn ->
case fetch_spec(path) do
{:ok, chain_spec} ->
case variant do
EthereumJSONRPC.Geth ->
{:ok, _} = GethImporter.import_genesis_accounts(chain_spec)
chain_spec = fetch_chain_spec(chain_spec_path)
precompiles_config = fetch_precompiles_config(precompiled_config_path)
_ ->
Importer.import_emission_rewards(chain_spec)
{:ok, _} = Importer.import_genesis_accounts(chain_spec)
extended_chain_spec = extend_chain_spec(chain_spec, precompiles_config, variant)
import_genesis_accounts(extended_chain_spec, variant)
import_precompiles_sources_and_abi(precompiles_config)
end)
end
end
@spec get_path(atom()) :: nil | binary()
defp get_path(key) do
case Application.get_env(:explorer, __MODULE__)[key] do
nil -> nil
value when is_binary(value) -> value
end
end
# Retrieves the chain specification, returning an empty map if unsuccessful.
@spec fetch_chain_spec(binary() | nil) :: map() | list()
defp fetch_chain_spec(path) do
case do_fetch(path, "Failed to fetch chain spec.") do
nil -> %{}
value -> value
end
end
# Retrieves the precompiled contracts configuration, returning an empty list if unsuccessful.
@spec fetch_precompiles_config(binary() | nil) :: list()
defp fetch_precompiles_config(path) do
case do_fetch(path, "Failed to fetch precompiles config.") do
nil -> []
value -> value
end
end
# Fetches JSON data from a specified path.
@spec do_fetch(binary() | nil, binary()) :: list() | map() | nil
defp do_fetch(path, warn_message_prefix) do
if path do
case fetch_spec_as_json(path) do
{:ok, chain_spec} ->
chain_spec
{:error, reason} ->
# credo:disable-for-next-line
Logger.warn(fn -> "Failed to fetch genesis data. #{inspect(reason)}" end)
# credo:disable-for-next-line Credo.Check.Refactor.Nesting
Logger.warn(fn -> "#{warn_message_prefix} #{inspect(reason)}" end)
nil
end
end)
else
Logger.warn(fn -> "Failed to fetch genesis data. Chain spec path is not set." end)
nil
end
end
defp fetch_spec(path) do
# Retrieves a JSON data from either a file or URL based on the source.
@spec fetch_spec_as_json(binary()) :: {:ok, list() | map()} | {:error, any()}
defp fetch_spec_as_json(path) do
if Helper.valid_url?(path) do
fetch_from_url(path)
else
@ -97,6 +160,8 @@ defmodule Explorer.ChainSpec.GenesisData do
end
end
# Reads and parses JSON data from a file.
@spec fetch_from_file(binary()) :: {:ok, list() | map()} | {:error, Jason.DecodeError.t()}
# sobelow_skip ["Traversal"]
defp fetch_from_file(path) do
with {:ok, data} <- File.read(path) do
@ -104,6 +169,8 @@ defmodule Explorer.ChainSpec.GenesisData do
end
end
# Fetches JSON data from a provided URL.
@spec fetch_from_url(binary()) :: {:ok, list() | map()} | {:error, Jason.DecodeError.t() | HTTPoison.Error.t()}
defp fetch_from_url(url) do
case HTTPoison.get(url) do
{:ok, %Response{body: body, status_code: 200}} ->
@ -113,4 +180,140 @@ defmodule Explorer.ChainSpec.GenesisData do
{:error, reason}
end
end
# Extends the chain specification with precompiled contract information.
#
# This function modifies the chain specification to include precompiled
# contracts that are not originally listed in the spec. It handles different
# formats of chain specs (list or map) according to the `variant` specified
# and adds precompiles.
#
# ## Parameters
# - `chain_spec`: The original chain specification in map or list format.
# - `precompiles_config`: A list of precompiled contracts to be added.
# - `variant`: The client variant (e.g., Geth or Parity), which dictates the
# spec structure.
#
# ## Returns
# - The modified chain specification with precompiled contracts included.
@spec extend_chain_spec(map() | list(), list(), EthereumJSONRPC.Geth | EthereumJSONRPC.Parity) :: map() | list()
defp extend_chain_spec(chain_spec, [], _) do
chain_spec
end
# Resulting spec will be handled by Explorer.ChainSpec.Geth.Importer
defp extend_chain_spec(chain_spec, precompiles_config, variant)
when is_list(chain_spec) and variant == EthereumJSONRPC.Geth do
precompiles_as_map =
precompiles_config
|> Enum.reduce(%{}, fn contract, acc ->
Map.put(acc, contract["address"], %{
"address" => contract["address"],
"balance" => 0,
"bytecode" => contract["bytecode"]
})
end)
filtered_maps_of_precompiles =
chain_spec
|> Enum.reduce(precompiles_as_map, fn account, acc ->
Map.delete(acc, account["address"])
end)
chain_spec ++ Map.values(filtered_maps_of_precompiles)
end
# Resulting spec will be handled by Explorer.ChainSpec.Geth.Importer
defp extend_chain_spec(%{"genesis" => sub_entity} = chain_spec, precompiles_config, variant)
when variant == EthereumJSONRPC.Geth do
updated_sub_entity = extend_chain_spec(sub_entity, precompiles_config, variant)
Map.put(chain_spec, "genesis", updated_sub_entity)
end
# Resulting spec will be handled by Explorer.ChainSpec.Geth.Importer
defp extend_chain_spec(chain_spec, precompiles_config, variant)
when is_map(chain_spec) and variant == EthereumJSONRPC.Geth do
accounts =
case chain_spec["alloc"] do
nil -> %{}
value -> value
end
updated_accounts =
precompiles_config
|> Enum.reduce(accounts, fn contract, acc ->
Map.put_new(acc, contract["address"], %{"balance" => 0, "code" => contract["bytecode"]})
end)
Map.put(chain_spec, "alloc", updated_accounts)
end
# Resulting spec will be handled by Explorer.ChainSpec.Parity.Importer
defp extend_chain_spec(chain_spec, precompiles_config, _) when is_map(chain_spec) do
accounts =
case chain_spec["accounts"] do
nil -> %{}
value -> value
end
updated_accounts =
precompiles_config
|> Enum.reduce(accounts, fn contract, acc ->
Map.put_new(acc, contract["address"], %{"balance" => 0, "constructor" => contract["bytecode"]})
end)
Map.put(chain_spec, "accounts", updated_accounts)
end
# Imports genesis accounts from the specified chain specification and updates
# `Explorer.Chain.Address` and `Explorer.Chain.Address.CoinBalance`, and
# `Explorer.Chain.Address.CoinBalanceDaily`.
@spec import_genesis_accounts(map() | list(), EthereumJSONRPC.Geth | EthereumJSONRPC.Parity) :: any()
defp import_genesis_accounts(chain_spec, variant) do
if not Enum.empty?(chain_spec) do
case variant do
EthereumJSONRPC.Geth ->
{:ok, _} = GethImporter.import_genesis_accounts(chain_spec)
_ ->
Importer.import_emission_rewards(chain_spec)
{:ok, _} = Importer.import_genesis_accounts(chain_spec)
end
end
end
# Iterates through the list of precompiles descriptions, and creating/updating
# each smart contract.
@spec import_precompiles_sources_and_abi([map()]) :: any()
defp import_precompiles_sources_and_abi(precompiles_config) do
precompiles_config
|> Enum.each(fn contract ->
attrs = %{
address_hash: contract["address"],
name: contract["name"],
file_path: nil,
compiler_version: contract["compiler"],
evm_version: nil,
optimization_runs: nil,
optimization: false,
contract_source_code: contract["source"],
constructor_arguments: nil,
external_libraries: [],
secondary_sources: [],
abi: Jason.decode!(contract["abi"]),
verified_via_sourcify: false,
verified_via_eth_bytecode_db: false,
verified_via_verifier_alliance: false,
partially_verified: false,
is_vyper_contract: false,
autodetect_constructor_args: nil,
is_yul: false,
compiler_settings: nil,
license_type: :none
}
SolidityPublisher.create_or_update_smart_contract(contract["address"], attrs)
end)
end
end

@ -1,4 +1,3 @@
# credo:disable-for-this-file
defmodule Explorer.ChainSpec.Geth.Importer do
@moduledoc """
Imports data from Geth genesis.json.
@ -10,7 +9,28 @@ defmodule Explorer.ChainSpec.Geth.Importer do
alias Explorer.{Chain, Helper}
alias Explorer.Chain.Hash.Address
@doc """
Imports genesis accounts into the database from a chain specification.
This function extracts genesis account information from a given chain specification,
including initial balances and contract bytecode. It enriches this data with additional
metadata, such as setting the block number to 0 (for genesis accounts) and determining
the day based on the timestamp of the first block. Subsequently, it imports the data
into `Explorer.Chain.Address`, `Explorer.Chain.Address.CoinBalance`, and
`Explorer.Chain.Address.CoinBalanceDaily` tables.
## Parameters
- `chain_spec`: A map or list representing the chain specification that contains
genesis account information. It may be structured directly as an
account list or as part of a larger specification map.
## Returns
- N/A
"""
@spec import_genesis_accounts(map() | list()) :: any()
def import_genesis_accounts(chain_spec) do
# credo:disable-for-previous-line Credo.Check.Design.DuplicatedCode
# It duplicates `import_genesis_accounts/1` from `Explorer.ChainSpec.Parity.Importer`
balance_params =
chain_spec
|> genesis_accounts()
@ -50,7 +70,32 @@ defmodule Explorer.ChainSpec.Geth.Importer do
Chain.import(params)
end
@spec genesis_accounts(any()) :: [%{address_hash: Address.t(), value: integer(), contract_code: String.t()}]
@doc """
Parses and returns the genesis account information from a chain specification.
It extracts account data such as address hashes, initial balances, and
optionally, contract bytecode for accounts defined in the genesis block of
a blockchain configuration.
## Parameters
- `input`: Can be a list of account maps or a map of the entire chain specification.
## Returns
- A list of maps, each representing an account with keys for the address hash,
balance , and optionally, the contract bytecode. Accounts without defined
balances are omitted.
### Usage
- `genesis_accounts(%{"genesis" => genesis_data})`: Extracts accounts from
a nested genesis key.
- `genesis_accounts(chain_spec)`: Parses accounts from a chain specification that
includes an 'alloc' key.
- `genesis_accounts(list_of_accounts)`: Directly parses a list of account data.
Intended to be called after `genesis_accounts(%{"genesis" => genesis_data})` call.
"""
@spec genesis_accounts(map() | list()) :: [
%{address_hash: Address.t(), value: non_neg_integer(), contract_code: String.t()}
]
def genesis_accounts(%{"genesis" => genesis}) do
genesis_accounts(genesis)
end
@ -80,6 +125,17 @@ defmodule Explorer.ChainSpec.Geth.Importer do
end
end
# Parses account data from a provided map to extract address, balance, and optional contract code.
#
# ## Parameters
# - `accounts`: A map with accounts data.
#
# ## Returns
# - A list of maps with accounts data including address hashes, balances,
# and any associated contract code.
@spec parse_accounts(%{binary() => map()}) :: [
%{:address_hash => Address.t(), value: non_neg_integer(), contract_code: String.t() | nil}
]
defp parse_accounts(accounts) do
accounts
|> Stream.filter(fn {_address, map} ->

@ -199,7 +199,35 @@ defmodule Explorer.SmartContract.Solidity.Publisher do
create_or_update_smart_contract(address_hash, attrs)
end
defp create_or_update_smart_contract(address_hash, attrs) do
@doc """
Creates or updates a smart contract record based on its verification status.
This function first checks if a smart contract associated with the provided address hash
is already verified. If verified, it updates the existing smart contract record with the
new attributes provided, such as external libraries and secondary sources. During the update,
the contract methods are also updated: existing methods are preserved, and any new methods
from the provided ABI are added to ensure the contract's integrity and completeness.
If the smart contract is not verified, it creates a new record in the database with the
provided attributes, setting it up for verification. In this case, all contract methods
from the ABI are freshly inserted as part of the new smart contract creation.
## Parameters
- `address_hash`: The hash of the address for the smart contract.
- `attrs`: A map containing attributes such as external libraries and secondary sources.
## Returns
- `{:ok, Explorer.Chain.SmartContract.t()}`: Successfully created or updated smart
contract.
- `{:error, data}`: on failure, returning `Ecto.Changeset.t()` or, if any issues
happen during setting the address as verified, an error message.
"""
@spec create_or_update_smart_contract(binary() | Explorer.Chain.Hash.t(), %{
:external_libraries => list(),
:secondary_sources => list(),
optional(any()) => any()
}) :: {:error, Ecto.Changeset.t() | String.t()} | {:ok, Explorer.Chain.SmartContract.t()}
def create_or_update_smart_contract(address_hash, attrs) do
Logger.info("Publish successfully verified Solidity smart-contract #{address_hash} into the DB")
if SmartContract.verified?(address_hash) do

File diff suppressed because one or more lines are too long

@ -246,10 +246,23 @@ config :explorer, Explorer.Chain.Events.Listener,
else: true
)
precompiled_config_base_dir =
case config_env() do
:prod -> "/app/"
_ -> "./"
end
precompiled_config_default_path =
case ConfigHelper.chain_type() do
"arbitrum" -> "#{precompiled_config_base_dir}config/assets/precompiles-arbitrum.json"
_ -> nil
end
config :explorer, Explorer.ChainSpec.GenesisData,
chain_spec_path: System.get_env("CHAIN_SPEC_PATH"),
emission_format: System.get_env("EMISSION_FORMAT", "DEFAULT"),
rewards_contract_address: System.get_env("REWARDS_CONTRACT", "0xeca443e8e1ab29971a45a9c57a6a9875701698a5")
rewards_contract_address: System.get_env("REWARDS_CONTRACT", "0xeca443e8e1ab29971a45a9c57a6a9875701698a5"),
precompiled_config_path: System.get_env("PRECOMPILED_CONTRACTS_CONFIG_PATH", precompiled_config_default_path)
address_sum_global_ttl = ConfigHelper.parse_time_env_var("CACHE_ADDRESS_SUM_PERIOD", "1h")

@ -85,3 +85,4 @@ COPY --from=builder /opt/release/blockscout .
COPY --from=builder /app/apps/explorer/node_modules ./node_modules
COPY --from=builder /app/config/config_helper.exs ./config/config_helper.exs
COPY --from=builder /app/config/config_helper.exs /app/releases/${RELEASE_VERSION}/config_helper.exs
COPY --from=builder /app/config/assets/precompiles-arbitrum.json ./config/assets/precompiles-arbitrum.json

Loading…
Cancel
Save