diff --git a/.dialyzer-ignore b/.dialyzer-ignore
index 26edbc4103..d43b267c9b 100644
--- a/.dialyzer-ignore
+++ b/.dialyzer-ignore
@@ -5,8 +5,10 @@
:0: Unknown type 'Elixir.Address':t/0
apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex:400: Function timestamp_to_datetime/1 has no local return
lib/explorer/repo/prometheus_logger.ex:8
-lib/explorer/smart_contract/publisher_worker.ex:1
-lib/explorer/smart_contract/publisher_worker.ex:6
+lib/explorer/smart_contract/solidity/publisher_worker.ex:1
+lib/explorer/smart_contract/vyper/publisher_worker.ex:1
+lib/explorer/smart_contract/solidity/publisher_worker.ex:6
+lib/explorer/smart_contract/vyper/publisher_worker.ex:6
apps/explorer/lib/explorer/repo/prometheus_logger.ex:8: Function microseconds_time/1 has no local return
apps/explorer/lib/explorer/repo/prometheus_logger.ex:8: The call 'Elixir.System':convert_time_unit(__@1::any(),'native','microseconds') breaks the contract (integer(),time_unit() | 'native',time_unit() | 'native') -> integer()
lib/block_scout_web/router.ex:1
@@ -21,7 +23,7 @@ lib/explorer/smart_contract/reader.ex:440
lib/indexer/fetcher/token_total_supply_on_demand.ex:16
lib/explorer/exchange_rates/source.ex:110
lib/explorer/exchange_rates/source.ex:113
-lib/explorer/smart_contract/verifier.ex:89
+lib/explorer/smart_contract/solidity/verifier.ex:89
lib/block_scout_web/templates/address_contract/index.html.eex:156
lib/block_scout_web/templates/address_contract/index.html.eex:199
lib/explorer/staking/stake_snapshotting.ex:15: Function do_snapshotting/7 has no local return
diff --git a/.github/workflows/config.yml b/.github/workflows/config.yml
index b59521c32d..e9b3a8fa5d 100644
--- a/.github/workflows/config.yml
+++ b/.github/workflows/config.yml
@@ -38,7 +38,7 @@ jobs:
path: |
deps
_build
- key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_2-${{ hashFiles('mix.lock') }}
+ key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_3-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-
@@ -98,7 +98,7 @@ jobs:
path: |
deps
_build
- key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_2-${{ hashFiles('mix.lock') }}
+ key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_3-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@@ -122,7 +122,7 @@ jobs:
path: |
deps
_build
- key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_2-${{ hashFiles('mix.lock') }}
+ key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_3-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@@ -145,7 +145,7 @@ jobs:
path: |
deps
_build
- key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_2-${{ hashFiles('mix.lock') }}
+ key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_3-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@@ -154,7 +154,7 @@ jobs:
id: dialyzer-cache
with:
path: priv/plts
- key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-dialyzer-mixlockhash_2-${{ hashFiles('mix.lock') }}
+ key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-dialyzer-mixlockhash_3-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-dialyzer-"
@@ -185,7 +185,7 @@ jobs:
path: |
deps
_build
- key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_2-${{ hashFiles('mix.lock') }}
+ key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_3-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@@ -211,7 +211,7 @@ jobs:
path: |
deps
_build
- key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_2-${{ hashFiles('mix.lock') }}
+ key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_3-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@@ -239,7 +239,7 @@ jobs:
path: |
deps
_build
- key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_2-${{ hashFiles('mix.lock') }}
+ key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_3-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@@ -288,7 +288,7 @@ jobs:
path: |
deps
_build
- key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_2-${{ hashFiles('mix.lock') }}
+ key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_3-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@@ -350,7 +350,7 @@ jobs:
path: |
deps
_build
- key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_2-${{ hashFiles('mix.lock') }}
+ key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_3-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@@ -406,7 +406,7 @@ jobs:
path: |
deps
_build
- key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_2-${{ hashFiles('mix.lock') }}
+ key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_3-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@@ -473,7 +473,7 @@ jobs:
path: |
deps
_build
- key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_2-${{ hashFiles('mix.lock') }}
+ key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_3-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@@ -534,7 +534,7 @@ jobs:
path: |
deps
_build
- key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_2-${{ hashFiles('mix.lock') }}
+ key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_3-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 930e257192..9d73c0ca4d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,7 @@
## Current
### Features
+- [#4745](https://github.com/blockscout/blockscout/pull/4745) - Vyper contracts verification
- [#4667](https://github.com/blockscout/blockscout/pull/4667) - Transaction Page: Add expand/collapse button for long contract method data
- [#4641](https://github.com/blockscout/blockscout/pull/4641), [#4733](https://github.com/blockscout/blockscout/pull/4733) - Improve Read Contract page logic
- [#4660](https://github.com/blockscout/blockscout/pull/4660) - Save Sourcify path instead of filename
diff --git a/apps/block_scout_web/assets/js/pages/verification_form.js b/apps/block_scout_web/assets/js/pages/verification_form.js
index a55f9ee4f4..15c520a565 100644
--- a/apps/block_scout_web/assets/js/pages/verification_form.js
+++ b/apps/block_scout_web/assets/js/pages/verification_form.js
@@ -233,6 +233,7 @@ if ($contractVerificationPage.length) {
if ($(this).prop('checked')) {
$('#verify_via_flattened_code_button').show()
$('#verify_via_sourcify_button').hide()
+ $('#verify_vyper_contract_button').hide()
}
})
@@ -240,6 +241,15 @@ if ($contractVerificationPage.length) {
if ($(this).prop('checked')) {
$('#verify_via_flattened_code_button').hide()
$('#verify_via_sourcify_button').show()
+ $('#verify_vyper_contract_button').hide()
+ }
+ })
+
+ $('.verify-vyper-contract').on('click', function () {
+ if ($(this).prop('checked')) {
+ $('#verify_via_flattened_code_button').hide()
+ $('#verify_via_sourcify_button').hide()
+ $('#verify_vyper_contract_button').show()
}
})
}
diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json
index 171f350fae..afed29818d 100644
--- a/apps/block_scout_web/assets/package-lock.json
+++ b/apps/block_scout_web/assets/package-lock.json
@@ -77,7 +77,8 @@
},
"../../../deps/phoenix": {
"version": "1.5.13",
- "integrity": "sha512-aJKbnuCTtgH8qT7AzHhPwhOP3bqhja3ogFMQmUdY7jNzl/91nsUtpnz0M3HDW7j+IvfjiM2/TIkOaGwzW9BKhg=="
+ "integrity": "sha512-aJKbnuCTtgH8qT7AzHhPwhOP3bqhja3ogFMQmUdY7jNzl/91nsUtpnz0M3HDW7j+IvfjiM2/TIkOaGwzW9BKhg==",
+ "license": "MIT"
},
"../../../deps/phoenix_html": {
"version": "2.14.3",
diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_controller.ex
index 408df30794..ccb9c12fc2 100644
--- a/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_controller.ex
+++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_controller.ex
@@ -7,7 +7,9 @@ defmodule BlockScoutWeb.AddressContractVerificationController do
alias Explorer.Chain
alias Explorer.Chain.Events.Publisher, as: EventsPublisher
alias Explorer.Chain.SmartContract
- alias Explorer.SmartContract.{PublisherWorker, Solidity.CodeCompiler, Solidity.CompilerVersion}
+ alias Explorer.SmartContract.{CompilerVersion, Solidity.CodeCompiler}
+ alias Explorer.SmartContract.Solidity.PublisherWorker, as: SolidityPublisherWorker
+ alias Explorer.SmartContract.Vyper.PublisherWorker, as: VyperPublisherWorker
alias Explorer.ThirdPartyIntegrations.Sourcify
def new(conn, %{"address_id" => address_hash_string}) do
@@ -26,7 +28,7 @@ defmodule BlockScoutWeb.AddressContractVerificationController do
)
compiler_versions =
- case CompilerVersion.fetch_versions() do
+ case CompilerVersion.fetch_versions(:solc) do
{:ok, compiler_versions} ->
compiler_versions
@@ -50,7 +52,18 @@ defmodule BlockScoutWeb.AddressContractVerificationController do
"external_libraries" => external_libraries
}
) do
- Que.add(PublisherWorker, {smart_contract["address_hash"], smart_contract, external_libraries, conn})
+ Que.add(SolidityPublisherWorker, {smart_contract["address_hash"], smart_contract, external_libraries, conn})
+
+ send_resp(conn, 204, "")
+ end
+
+ def create(
+ conn,
+ %{
+ "smart_contract" => smart_contract
+ }
+ ) do
+ Que.add(VyperPublisherWorker, {smart_contract["address_hash"], smart_contract, conn})
send_resp(conn, 204, "")
end
@@ -104,7 +117,7 @@ defmodule BlockScoutWeb.AddressContractVerificationController do
end
def create(conn, _params) do
- Que.add(PublisherWorker, {"", %{}, %{}, conn})
+ Que.add(SolidityPublisherWorker, {"", %{}, %{}, conn})
send_resp(conn, 204, "")
end
diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_via_flattened_code_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_via_flattened_code_controller.ex
index 2427d82aed..e36ef050f5 100644
--- a/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_via_flattened_code_controller.ex
+++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_via_flattened_code_controller.ex
@@ -4,7 +4,7 @@ defmodule BlockScoutWeb.AddressContractVerificationViaFlattenedCodeController do
alias BlockScoutWeb.Controller
alias Explorer.Chain
alias Explorer.Chain.SmartContract
- alias Explorer.SmartContract.{PublisherWorker, Solidity.CodeCompiler, Solidity.CompilerVersion}
+ alias Explorer.SmartContract.{CompilerVersion, Solidity.CodeCompiler, Solidity.PublisherWorker}
def new(conn, %{"address_id" => address_hash_string}) do
if Chain.smart_contract_fully_verified?(address_hash_string) do
@@ -22,7 +22,7 @@ defmodule BlockScoutWeb.AddressContractVerificationViaFlattenedCodeController do
)
compiler_versions =
- case CompilerVersion.fetch_versions() do
+ case CompilerVersion.fetch_versions(:solc) do
{:ok, compiler_versions} ->
compiler_versions
diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_vyper_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_vyper_controller.ex
new file mode 100644
index 0000000000..f1cb3832e2
--- /dev/null
+++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_vyper_controller.ex
@@ -0,0 +1,47 @@
+defmodule BlockScoutWeb.AddressContractVerificationVyperController do
+ use BlockScoutWeb, :controller
+
+ alias Explorer.Chain.SmartContract
+ alias Explorer.SmartContract.{CompilerVersion, Vyper.PublisherWorker}
+
+ def new(conn, %{"address_id" => address_hash_string}) do
+ changeset =
+ SmartContract.changeset(
+ %SmartContract{address_hash: address_hash_string},
+ %{}
+ )
+
+ compiler_versions =
+ case CompilerVersion.fetch_versions(:vyper) do
+ {:ok, compiler_versions} ->
+ compiler_versions
+
+ {:error, _} ->
+ []
+ end
+
+ render(conn, "new.html",
+ changeset: changeset,
+ compiler_versions: compiler_versions,
+ address_hash: address_hash_string
+ )
+ end
+
+ def create(
+ conn,
+ %{
+ "smart_contract" => smart_contract
+ }
+ ) do
+ Que.add(PublisherWorker, {smart_contract["address_hash"], smart_contract, conn})
+
+ send_resp(conn, 204, "")
+ end
+
+ def parse_optimization_runs(%{"runs" => runs}) do
+ case Integer.parse(runs) do
+ {integer, ""} -> integer
+ _ -> 200
+ end
+ end
+end
diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex
index 362b146a0e..fe3be0807e 100644
--- a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex
+++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex
@@ -6,7 +6,8 @@ defmodule BlockScoutWeb.API.RPC.ContractController do
alias Explorer.Chain
alias Explorer.Chain.Events.Publisher, as: EventsPublisher
alias Explorer.Chain.{Hash, SmartContract}
- alias Explorer.SmartContract.Publisher
+ alias Explorer.SmartContract.Solidity.Publisher
+ alias Explorer.SmartContract.Vyper.Publisher, as: VyperPublisher
alias Explorer.ThirdPartyIntegrations.Sourcify
def verify(conn, %{"addressHash" => address_hash} = params) do
@@ -196,6 +197,40 @@ defmodule BlockScoutWeb.API.RPC.ContractController do
end
end
+ def verify_vyper_contract(conn, %{"addressHash" => address_hash} = params) do
+ with {:params, {:ok, fetched_params}} <- {:params, fetch_vyper_verify_params(params)},
+ {:format, {:ok, casted_address_hash}} <- to_address_hash(address_hash),
+ {:publish, {:ok, _}} <-
+ {:publish, VyperPublisher.publish(address_hash, fetched_params)} do
+ address = Chain.address_hash_to_address_with_source_code(casted_address_hash)
+
+ render(conn, :verify, %{contract: address})
+ else
+ {:publish,
+ {:error,
+ %Ecto.Changeset{
+ errors: [
+ address_hash:
+ {"has already been taken",
+ [
+ constraint: :unique,
+ constraint_name: "smart_contracts_address_hash_index"
+ ]}
+ ]
+ }}} ->
+ render(conn, :error, error: "Smart-contract already verified.")
+
+ {:publish, _} ->
+ render(conn, :error, error: "Something went wrong while publishing the contract.")
+
+ {:format, :error} ->
+ render(conn, :error, error: "Invalid address hash")
+
+ {:params, {:error, error}} ->
+ render(conn, :error, error: error)
+ end
+ end
+
def publish_without_broadcast(
%{"addressHash" => address_hash, "abi" => abi, "compilationTargetFilePath" => file_path} = input
) do
@@ -416,6 +451,15 @@ defmodule BlockScoutWeb.API.RPC.ContractController do
|> parse_optimization_runs()
end
+ defp fetch_vyper_verify_params(params) do
+ {:ok, %{}}
+ |> required_param(params, "addressHash", "address_hash")
+ |> required_param(params, "name", "name")
+ |> required_param(params, "compilerVersion", "compiler_version")
+ |> required_param(params, "contractSourceCode", "contract_source_code")
+ |> optional_param(params, "constructorArguments", "constructor_arguments")
+ end
+
defp parse_optimization_runs({:ok, %{"optimization_runs" => runs} = opts}) when is_bitstring(runs) do
{:ok, Map.put(opts, "optimization_runs", 200)}
end
diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v1/verified_smart_contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v1/verified_smart_contract_controller.ex
index 07b1714141..59431742ad 100644
--- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v1/verified_smart_contract_controller.ex
+++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v1/verified_smart_contract_controller.ex
@@ -3,7 +3,7 @@ defmodule BlockScoutWeb.API.V1.VerifiedSmartContractController do
alias Explorer.Chain
alias Explorer.Chain.Hash.Address
- alias Explorer.SmartContract.Publisher
+ alias Explorer.SmartContract.Solidity.Publisher
def create(conn, params) do
with {:ok, hash} <- validate_address_hash(params["address_hash"]),
diff --git a/apps/block_scout_web/lib/block_scout_web/etherscan.ex b/apps/block_scout_web/lib/block_scout_web/etherscan.ex
index c0ef9edf86..c01023faad 100644
--- a/apps/block_scout_web/lib/block_scout_web/etherscan.ex
+++ b/apps/block_scout_web/lib/block_scout_web/etherscan.ex
@@ -2505,6 +2505,76 @@ defmodule BlockScoutWeb.Etherscan do
]
}
+ @contract_verify_vyper_contract_action %{
+ name: "verify_vyper_contract",
+ description: """
+ Verify a vyper contract with its source code and contract creation information.
+
+
+
curl POST example:
+
+
+
+
+
+ curl --location --request POST 'http://localhost:4000/api?module=contract&action=verify_vyper_contract' \
+ --form 'contractSourceCode="SOURCE_CODE"' \
+ --form 'name="Vyper_contract"' \
+ --form 'addressHash="0xE60B1B8bD493569a3E945be50A6c89d29a560Fa1"' \
+ --form 'compilerVersion="v0.2.12"'
+
+
+
+
+ """,
+ required_params: [
+ %{
+ key: "addressHash",
+ placeholder: "addressHash",
+ type: "string",
+ description: "The address of the contract."
+ },
+ %{
+ key: "name",
+ placeholder: "name",
+ type: "string",
+ description: "The name of the contract."
+ },
+ %{
+ key: "compilerVersion",
+ placeholder: "compilerVersion",
+ type: "string",
+ description: "The compiler version for the contract."
+ },
+ %{
+ key: "contractSourceCode",
+ placeholder: "contractSourceCode",
+ type: "string",
+ description: "The source code of the contract."
+ }
+ ],
+ optional_params: [
+ %{
+ key: "constructorArguments",
+ type: "string",
+ description: "The constructor argument data provided."
+ }
+ ],
+ responses: [
+ %{
+ code: "200",
+ description: "successful operation",
+ example_value: Jason.encode!(@contract_verify_example_value),
+ type: "model",
+ model: @contract_model
+ },
+ %{
+ code: "200",
+ description: "error",
+ example_value: Jason.encode!(@contract_verify_example_value_error)
+ }
+ ]
+ }
@contract_getabi_action %{
name: "getabi",
description: "Get ABI for verified contract. Also available through a GraphQL 'addresses' query.",
@@ -2750,7 +2820,8 @@ defmodule BlockScoutWeb.Etherscan do
@contract_getabi_action,
@contract_getsourcecode_action,
@contract_verify_action,
- @contract_verify_via_sourcify_action
+ @contract_verify_via_sourcify_action,
+ @contract_verify_vyper_contract_action
]
}
diff --git a/apps/block_scout_web/lib/block_scout_web/notifier.ex b/apps/block_scout_web/lib/block_scout_web/notifier.ex
index b74d2f9b1d..f3d5b22ba3 100644
--- a/apps/block_scout_web/lib/block_scout_web/notifier.ex
+++ b/apps/block_scout_web/lib/block_scout_web/notifier.ex
@@ -8,6 +8,7 @@ defmodule BlockScoutWeb.Notifier do
alias BlockScoutWeb.{
AddressContractVerificationViaFlattenedCodeView,
AddressContractVerificationViaJsonView,
+ AddressContractVerificationVyperView,
Endpoint
}
@@ -17,7 +18,7 @@ defmodule BlockScoutWeb.Notifier do
alias Explorer.Chain.Transaction.History.TransactionStats
alias Explorer.Counters.AverageBlockTime
alias Explorer.ExchangeRates.Token
- alias Explorer.SmartContract.{Solidity.CodeCompiler, Solidity.CompilerVersion}
+ alias Explorer.SmartContract.{CompilerVersion, Solidity.CodeCompiler}
alias Phoenix.View
def handle_event({:chain_event, :addresses, type, addresses}) when type in [:realtime, :on_demand] do
@@ -47,6 +48,8 @@ defmodule BlockScoutWeb.Notifier do
{:chain_event, :contract_verification_result, :on_demand, {address_hash, contract_verification_result, conn}}
) do
verification_from_json_upload? = Map.has_key?(conn.params, "file")
+ verification_from_flattened_source? = Map.has_key?(conn.params, "external_libraries")
+ compiler = if verification_from_flattened_source?, do: :solc, else: :vyper
contract_verification_result =
case contract_verification_result do
@@ -55,7 +58,7 @@ defmodule BlockScoutWeb.Notifier do
{:error, changeset} ->
compiler_versions =
- case CompilerVersion.fetch_versions() do
+ case CompilerVersion.fetch_versions(compiler) do
{:ok, compiler_versions} ->
compiler_versions
@@ -64,10 +67,10 @@ defmodule BlockScoutWeb.Notifier do
end
view =
- if verification_from_json_upload? do
- AddressContractVerificationViaJsonView
- else
- AddressContractVerificationViaFlattenedCodeView
+ cond do
+ verification_from_json_upload? -> AddressContractVerificationViaJsonView
+ verification_from_flattened_source? -> AddressContractVerificationViaFlattenedCodeView
+ true -> AddressContractVerificationVyperView
end
result =
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex
index 53d16f1e32..3c194b07bf 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex
@@ -51,7 +51,7 @@
<%= gettext "Optimization enabled" %>
-
<%= format_optimization_text(target_contract.optimization) %>
+
<%= if target_contract.is_vyper_contract, do: "N/A", else: format_optimization_text(target_contract.optimization) %>
- <%= gettext "Compiler version" %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification/new.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification/new.html.eex
index 51795dfb93..626058e18c 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification/new.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification/new.html.eex
@@ -26,18 +26,25 @@
@@ -86,6 +95,14 @@
style: "display: none;",
"data-button-loading": "animation"
) %>
+ <%= link(
+ gettext("Next"),
+ to: address_verify_vyper_contract_path(@conn, :new, @address_hash),
+ id: "verify_vyper_contract_button",
+ class: "btn-full-primary mr-2",
+ style: "display: none;",
+ "data-button-loading": "animation"
+ ) %>
<%=
link(
gettext("Cancel"),
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex
index dfa2f7e91b..501729afab 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex
@@ -15,7 +15,7 @@
-
<%= gettext "New Smart Contract Verification" %>
+
<%= gettext "New Solidity Smart Contract Verification" %>
<%= form_for @changeset,
address_contract_verification_path(@conn, :create),
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex
new file mode 100644
index 0000000000..d4624055c0
--- /dev/null
+++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex
@@ -0,0 +1,102 @@
+<% metadata_for_verification = Chain.get_address_verified_twin_contract(@address_hash).verified_contract %>
+<% contract_name_value = if metadata_for_verification, do: metadata_for_verification.name, else: "Vyper_contract" %>
+<% compiler_version = if metadata_for_verification, do: metadata_for_verification.compiler_version, else: "latest" %>
+<% contract_source_code_value = if metadata_for_verification, do: metadata_for_verification.contract_source_code, else: "" %>
+
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_logs/_logs.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_logs/_logs.html.eex
index 21ec3c2ef3..90d009f214 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/address_logs/_logs.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/address_logs/_logs.html.eex
@@ -6,13 +6,7 @@
<%= gettext "To see accurate decoded input data, the contract must be verified." %>
<%= case @log.transaction do %>
<% %{to_address: %{hash: hash}} -> %>
- <% path =
- if Application.get_env(:explorer, Explorer.ThirdPartyIntegrations.Sourcify)[:enabled] do
- address_verify_contract_path(@conn, :new, hash)
- else
- address_verify_contract_via_flattened_code_path(@conn, :new, hash)
- end
- %>
+ <% path = address_verify_contract_path(@conn, :new, hash) %>
<%= gettext "Verify the contract " %>
<%= gettext "here" %>
<% _ -> %>
<%= nil %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/layout/app.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/layout/app.html.eex
index eb2f5c9187..6d21273458 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/layout/app.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/layout/app.html.eex
@@ -219,6 +219,7 @@
@view_module != Elixir.BlockScoutWeb.AddressContractVerificationView &&
@view_module != Elixir.BlockScoutWeb.AddressContractVerificationViaJsonView &&
@view_module != Elixir.BlockScoutWeb.AddressContractVerificationViaFlattenedCodeView &&
+ @view_module != Elixir.BlockScoutWeb.AddressContractVerificationVyperView &&
@view_module != Elixir.BlockScoutWeb.AddressReadContractView &&
@view_module != Elixir.BlockScoutWeb.AddressReadProxyView &&
@view_module != Elixir.BlockScoutWeb.AddressWriteContractView &&
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex
index 24ac148187..13b4607b24 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex
@@ -5,13 +5,7 @@
<%= if minimal_proxy_template do %>
<%= render BlockScoutWeb.CommonComponentsView, "_minimal_proxy_pattern.html", address_hash: metadata_for_verification.address_hash, conn: @conn %>
<% else %>
- <% path =
- if Application.get_env(:explorer, Explorer.ThirdPartyIntegrations.Sourcify)[:enabled] do
- address_verify_contract_path(@conn, :new, @address.hash)
- else
- address_verify_contract_via_flattened_code_path(@conn, :new, @address.hash)
- end
- %>
+ <% path = address_verify_contract_path(@conn, :new, @address.hash) %>
<%= render BlockScoutWeb.CommonComponentsView, "_info.html" %>
<%= gettext("Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB") %> <%= link(
metadata_for_verification.address_hash,
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_decoded_input.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_decoded_input.html.eex
index 184f647568..bf85407d05 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_decoded_input.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_decoded_input.html.eex
@@ -8,13 +8,7 @@
<%= gettext "To see accurate decoded input data, the contract must be verified." %>
<%= case @transaction do %>
<% %{to_address: %{hash: hash}} -> %>
- <% path =
- if Application.get_env(:explorer, Explorer.ThirdPartyIntegrations.Sourcify)[:enabled] do
- address_verify_contract_path(@conn, :new, hash)
- else
- address_verify_contract_via_flattened_code_path(@conn, :new, hash)
- end
- %>
+ <% path = address_verify_contract_path(@conn, :new, hash) %>
<%= gettext "Verify the contract " %><%= gettext "here" %>
<% _ -> %>
<%= nil %>
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction_log/_logs.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction_log/_logs.html.eex
index b44ae8d919..7141a37293 100644
--- a/apps/block_scout_web/lib/block_scout_web/templates/transaction_log/_logs.html.eex
+++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction_log/_logs.html.eex
@@ -6,13 +6,7 @@
<%= gettext "To see accurate decoded input data, the contract must be verified." %>
<%= case @transaction do %>
<% %{to_address: %{hash: hash}} -> %>
- <% path =
- if Application.get_env(:explorer, Explorer.ThirdPartyIntegrations.Sourcify)[:enabled] do
- address_verify_contract_path(@conn, :new, hash)
- else
- address_verify_contract_via_flattened_code_path(@conn, :new, hash)
- end
- %>
+ <% path = address_verify_contract_path(@conn, :new, hash) %>
<%= gettext "Verify the contract " %><%= gettext "here" %>
<% _ -> %>
<%= nil %>
diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_vyper_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_vyper_view.ex
new file mode 100644
index 0000000000..ea99d78b71
--- /dev/null
+++ b/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_vyper_view.ex
@@ -0,0 +1,5 @@
+defmodule BlockScoutWeb.AddressContractVerificationVyperView do
+ use BlockScoutWeb, :view
+
+ alias Explorer.Chain
+end
diff --git a/apps/block_scout_web/lib/block_scout_web/web_router.ex b/apps/block_scout_web/lib/block_scout_web/web_router.ex
index 2f199f2de8..ab8d6c9132 100644
--- a/apps/block_scout_web/lib/block_scout_web/web_router.ex
+++ b/apps/block_scout_web/lib/block_scout_web/web_router.ex
@@ -132,22 +132,6 @@ defmodule BlockScoutWeb.WebRouter do
as: :verify_contract
)
- # if Application.get_env(:explorer, Explorer.ThirdPartyIntegrations.Sourcify)[:enabled] do
- # resources(
- # "/contract_verifications",
- # AddressContractVerificationController,
- # only: [:new],
- # as: :verify_contract
- # )
- # else
- # resources(
- # "/contract_verifications",
- # AddressContractVerificationViaFlattenedCodeController,
- # only: [:new],
- # as: :verify_contract
- # )
- # end
-
resources(
"/verify-via-flattened-code",
AddressContractVerificationViaFlattenedCodeController,
@@ -162,6 +146,13 @@ defmodule BlockScoutWeb.WebRouter do
as: :verify_contract_via_json
)
+ resources(
+ "/verify-vyper-contract",
+ AddressContractVerificationVyperController,
+ only: [:new],
+ as: :verify_vyper_contract
+ )
+
resources(
"/read-contract",
AddressReadContractController,
diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot
index 5bb63e17c7..75da1b891c 100644
--- a/apps/block_scout_web/priv/gettext/default.pot
+++ b/apps/block_scout_web/priv/gettext/default.pot
@@ -125,6 +125,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:156
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:66
msgid "ABI-encoded Constructor Arguments (if required by the contract)"
msgstr ""
@@ -170,7 +171,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:16
-#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:26 lib/block_scout_web/views/address_view.ex:104
+#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:20 lib/block_scout_web/views/address_view.ex:104
msgid "Address"
msgstr ""
@@ -199,7 +200,7 @@ msgid "All"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:18
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:12
msgid "All functions displayed below are from ABI of that contract. In order to verify current contract, proceed with"
msgstr ""
@@ -434,9 +435,10 @@ msgid "Call Code"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:91
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:108
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:305
-#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:56 lib/block_scout_web/templates/api_docs/_action_tile.html.eex:47
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:56
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:93 lib/block_scout_web/templates/api_docs/_action_tile.html.eex:47
#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:54
msgid "Cancel"
msgstr ""
@@ -526,6 +528,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:71
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:44
msgid "Compiler"
msgstr ""
@@ -552,7 +555,8 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:4
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:13
-#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:4 lib/block_scout_web/templates/tokens/holder/index.html.eex:17
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:4
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:8 lib/block_scout_web/templates/tokens/holder/index.html.eex:17
msgid "Connection Lost"
msgstr ""
@@ -596,7 +600,8 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:18
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:27
-#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:13 lib/block_scout_web/views/address_view.ex:102
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:13
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:22 lib/block_scout_web/views/address_view.ex:102
msgid "Contract Address"
msgstr ""
@@ -629,12 +634,13 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:38
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:33
msgid "Contract Name"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:22
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:16
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:10
msgid "Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB"
msgstr ""
@@ -817,9 +823,9 @@ msgid "DApp for Staking %{symbol} tokens"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:107
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:101
#: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:7 lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:21
-#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:110
+#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:104
msgid "Data"
msgstr ""
@@ -840,10 +846,10 @@ msgid "Decimals"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:38
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:44 lib/block_scout_web/templates/address_logs/_logs.html.eex:59
-#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:39 lib/block_scout_web/templates/transaction_log/_logs.html.eex:47
-#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:62
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:32
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:38 lib/block_scout_web/templates/address_logs/_logs.html.eex:53
+#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:33 lib/block_scout_web/templates/transaction_log/_logs.html.eex:41
+#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:56
msgid "Decoded"
msgstr ""
@@ -943,8 +949,8 @@ msgid "ERC-721 "
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:95
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:95 lib/block_scout_web/templates/smart_contract/_functions.html.eex:131
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:89
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:89 lib/block_scout_web/templates/smart_contract/_functions.html.eex:125
msgid "ETH"
msgstr ""
@@ -1047,13 +1053,13 @@ msgid "External libraries"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:39
+#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:33
msgid "Failed to decode input data."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:41
-#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:42
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:35
+#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:36
msgid "Failed to decode log data."
msgstr ""
@@ -1067,11 +1073,6 @@ msgstr ""
msgid "Fetching tokens..."
msgstr ""
-#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:35
-msgid "Flattened source code"
-msgstr ""
-
#, elixir-format
#: lib/block_scout_web/templates/admin/dashboard/index.html.eex:16
msgid "For any existing contracts in the database, insert all ABI entries into the contract_methods table. Use this in case you have verified smart contracts before early March 2019 and you want other contracts with the same functions to show those ABI's as candidate matches."
@@ -1165,7 +1166,7 @@ msgid "However, in general, the"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:25
+#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:19
msgid "IMPORTANT: This information is a best guess based on similar functions from other verified contracts."
msgstr ""
@@ -1340,9 +1341,10 @@ msgid "Loading..."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:72
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:81
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:299
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:50
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:87
msgid "Loading...."
msgstr ""
@@ -1352,7 +1354,7 @@ msgid "Log Data"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:120
+#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:114
msgid "Log Index"
msgstr ""
@@ -1512,14 +1514,13 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:9
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:18
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:9
msgid "New Smart Contract Verification"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:75
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:82
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:84
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:91 lib/block_scout_web/templates/address_contract_verification/new.html.eex:99
msgid "Next"
msgstr ""
@@ -1672,7 +1673,7 @@ msgid "Potential Reward Share"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:24
+#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:18
msgid "Potential matches from our contract method database:"
msgstr ""
@@ -1710,7 +1711,7 @@ msgid "QR Code"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:99
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:93
msgid "Query"
msgstr ""
@@ -1776,6 +1777,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:302
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:53
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:90
msgid "Reset"
msgstr ""
@@ -1944,11 +1946,6 @@ msgstr ""
msgid "Sources and Metadata JSON"
msgstr ""
-#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:40
-msgid "Sourcify: Sources and metadata JSON file"
-msgstr ""
-
#, elixir-format
#: lib/block_scout_web/templates/stakes/_stakes_modal_stake.html.eex:7
msgid "Stake"
@@ -2089,7 +2086,7 @@ msgid "The block height of a particular block is defined as the number of blocks
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:43
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:37
msgid "The fallback function is executed on a call to the contract if none of the other functions match the given function signature, or if no data was supplied at all and there is no receive Ether function. The fallback function always receives data, but in order to also receive Ether it must be marked payable."
msgstr ""
@@ -2129,7 +2126,7 @@ msgid "The percentage of stake in a single pool relative to the total amount sta
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:45
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:39
msgid "The receive function is executed on a call to the contract with empty calldata. This is the function that is executed on plain Ether transfers (e.g. via .send() or .transfer()). If no such function exists, but a payable fallback function exists, the fallback function will be called on a plain Ether transfer. If neither a receive Ether nor a payable fallback function is present, the contract cannot receive Ether through regular transactions and throws an exception."
msgstr ""
@@ -2330,7 +2327,7 @@ msgid "To"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:26
+#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:20
msgid "To have guaranteed accuracy, use the link above to verify the contract's source code."
msgstr ""
@@ -2436,8 +2433,8 @@ msgid "Topic"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:77
-#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:80
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:71
+#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:74
msgid "Topics"
msgstr ""
@@ -2482,7 +2479,7 @@ msgid "Trade STAKE on BitMax"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:25
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:19
#: lib/block_scout_web/views/transaction_view.ex:463
msgid "Transaction"
msgstr ""
@@ -2711,19 +2708,20 @@ msgstr ""
#: lib/block_scout_web/templates/address_contract/index.html.eex:24
#: lib/block_scout_web/templates/address_contract/index.html.eex:158 lib/block_scout_web/templates/address_contract/index.html.eex:170
#: lib/block_scout_web/templates/address_contract/index.html.eex:201 lib/block_scout_web/templates/address_contract/index.html.eex:213
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:19
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:13
msgid "Verify & Publish"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:301
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:52
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:89
msgid "Verify & publish"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:16
-#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:18 lib/block_scout_web/templates/transaction_log/_logs.html.eex:16
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:10
+#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:12 lib/block_scout_web/templates/transaction_log/_logs.html.eex:10
msgid "Verify the contract "
msgstr ""
@@ -2774,7 +2772,7 @@ msgid "View transaction %{transaction} on %{subnetwork}"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:130
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:124
msgid "WEI"
msgstr ""
@@ -2824,7 +2822,7 @@ msgid "Working Stake Amount"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:99
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:93
msgid "Write"
msgstr ""
@@ -2939,7 +2937,7 @@ msgid "custom RPC"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:43
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:37
msgid "fallback"
msgstr ""
@@ -2949,8 +2947,8 @@ msgid "false"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:16
-#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:18 lib/block_scout_web/templates/transaction_log/_logs.html.eex:16
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:10
+#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:12 lib/block_scout_web/templates/transaction_log/_logs.html.eex:10
msgid "here"
msgstr ""
@@ -2970,7 +2968,7 @@ msgid "of"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:21
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:15
msgid "page"
msgstr ""
@@ -2980,7 +2978,7 @@ msgid "pool owner"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:45
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:39
msgid "receive"
msgstr ""
@@ -3046,3 +3044,33 @@ msgstr ""
#: lib/block_scout_web/templates/block/overview.html.eex:209
msgid "burned from transactions included in the block (Base fee (per unit of gas) * Gas Used)."
msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:55
+msgid "Enter the Vyper Contract Code"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:18
+msgid "New Solidity Smart Contract Verification"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:13
+msgid "New Vyper Smart Contract Verification"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:41
+msgid "Via Sourcify: Sources and metadata JSON file"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:35
+msgid "Via flattened source code"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:47
+msgid "Vyper contract"
+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 5bb63e17c7..75da1b891c 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
@@ -125,6 +125,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:156
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:66
msgid "ABI-encoded Constructor Arguments (if required by the contract)"
msgstr ""
@@ -170,7 +171,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:16
-#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:26 lib/block_scout_web/views/address_view.ex:104
+#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:20 lib/block_scout_web/views/address_view.ex:104
msgid "Address"
msgstr ""
@@ -199,7 +200,7 @@ msgid "All"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:18
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:12
msgid "All functions displayed below are from ABI of that contract. In order to verify current contract, proceed with"
msgstr ""
@@ -434,9 +435,10 @@ msgid "Call Code"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:91
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:108
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:305
-#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:56 lib/block_scout_web/templates/api_docs/_action_tile.html.eex:47
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:56
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:93 lib/block_scout_web/templates/api_docs/_action_tile.html.eex:47
#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:54
msgid "Cancel"
msgstr ""
@@ -526,6 +528,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:71
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:44
msgid "Compiler"
msgstr ""
@@ -552,7 +555,8 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:4
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:13
-#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:4 lib/block_scout_web/templates/tokens/holder/index.html.eex:17
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:4
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:8 lib/block_scout_web/templates/tokens/holder/index.html.eex:17
msgid "Connection Lost"
msgstr ""
@@ -596,7 +600,8 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:18
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:27
-#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:13 lib/block_scout_web/views/address_view.ex:102
+#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:13
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:22 lib/block_scout_web/views/address_view.ex:102
msgid "Contract Address"
msgstr ""
@@ -629,12 +634,13 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:38
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:33
msgid "Contract Name"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:22
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:16
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:10
msgid "Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB"
msgstr ""
@@ -817,9 +823,9 @@ msgid "DApp for Staking %{symbol} tokens"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:107
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:101
#: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:7 lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:21
-#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:110
+#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:104
msgid "Data"
msgstr ""
@@ -840,10 +846,10 @@ msgid "Decimals"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:38
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:44 lib/block_scout_web/templates/address_logs/_logs.html.eex:59
-#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:39 lib/block_scout_web/templates/transaction_log/_logs.html.eex:47
-#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:62
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:32
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:38 lib/block_scout_web/templates/address_logs/_logs.html.eex:53
+#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:33 lib/block_scout_web/templates/transaction_log/_logs.html.eex:41
+#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:56
msgid "Decoded"
msgstr ""
@@ -943,8 +949,8 @@ msgid "ERC-721 "
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:95
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:95 lib/block_scout_web/templates/smart_contract/_functions.html.eex:131
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:89
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:89 lib/block_scout_web/templates/smart_contract/_functions.html.eex:125
msgid "ETH"
msgstr ""
@@ -1047,13 +1053,13 @@ msgid "External libraries"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:39
+#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:33
msgid "Failed to decode input data."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:41
-#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:42
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:35
+#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:36
msgid "Failed to decode log data."
msgstr ""
@@ -1067,11 +1073,6 @@ msgstr ""
msgid "Fetching tokens..."
msgstr ""
-#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:35
-msgid "Flattened source code"
-msgstr ""
-
#, elixir-format
#: lib/block_scout_web/templates/admin/dashboard/index.html.eex:16
msgid "For any existing contracts in the database, insert all ABI entries into the contract_methods table. Use this in case you have verified smart contracts before early March 2019 and you want other contracts with the same functions to show those ABI's as candidate matches."
@@ -1165,7 +1166,7 @@ msgid "However, in general, the"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:25
+#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:19
msgid "IMPORTANT: This information is a best guess based on similar functions from other verified contracts."
msgstr ""
@@ -1340,9 +1341,10 @@ msgid "Loading..."
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:72
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:81
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:299
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:50
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:87
msgid "Loading...."
msgstr ""
@@ -1352,7 +1354,7 @@ msgid "Log Data"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:120
+#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:114
msgid "Log Index"
msgstr ""
@@ -1512,14 +1514,13 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:9
-#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:18
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:9
msgid "New Smart Contract Verification"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:75
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:82
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:84
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:91 lib/block_scout_web/templates/address_contract_verification/new.html.eex:99
msgid "Next"
msgstr ""
@@ -1672,7 +1673,7 @@ msgid "Potential Reward Share"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:24
+#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:18
msgid "Potential matches from our contract method database:"
msgstr ""
@@ -1710,7 +1711,7 @@ msgid "QR Code"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:99
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:93
msgid "Query"
msgstr ""
@@ -1776,6 +1777,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:302
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:53
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:90
msgid "Reset"
msgstr ""
@@ -1944,11 +1946,6 @@ msgstr ""
msgid "Sources and Metadata JSON"
msgstr ""
-#, elixir-format
-#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:40
-msgid "Sourcify: Sources and metadata JSON file"
-msgstr ""
-
#, elixir-format
#: lib/block_scout_web/templates/stakes/_stakes_modal_stake.html.eex:7
msgid "Stake"
@@ -2089,7 +2086,7 @@ msgid "The block height of a particular block is defined as the number of blocks
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:43
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:37
msgid "The fallback function is executed on a call to the contract if none of the other functions match the given function signature, or if no data was supplied at all and there is no receive Ether function. The fallback function always receives data, but in order to also receive Ether it must be marked payable."
msgstr ""
@@ -2129,7 +2126,7 @@ msgid "The percentage of stake in a single pool relative to the total amount sta
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:45
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:39
msgid "The receive function is executed on a call to the contract with empty calldata. This is the function that is executed on plain Ether transfers (e.g. via .send() or .transfer()). If no such function exists, but a payable fallback function exists, the fallback function will be called on a plain Ether transfer. If neither a receive Ether nor a payable fallback function is present, the contract cannot receive Ether through regular transactions and throws an exception."
msgstr ""
@@ -2330,7 +2327,7 @@ msgid "To"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:26
+#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:20
msgid "To have guaranteed accuracy, use the link above to verify the contract's source code."
msgstr ""
@@ -2436,8 +2433,8 @@ msgid "Topic"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:77
-#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:80
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:71
+#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:74
msgid "Topics"
msgstr ""
@@ -2482,7 +2479,7 @@ msgid "Trade STAKE on BitMax"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:25
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:19
#: lib/block_scout_web/views/transaction_view.ex:463
msgid "Transaction"
msgstr ""
@@ -2711,19 +2708,20 @@ msgstr ""
#: lib/block_scout_web/templates/address_contract/index.html.eex:24
#: lib/block_scout_web/templates/address_contract/index.html.eex:158 lib/block_scout_web/templates/address_contract/index.html.eex:170
#: lib/block_scout_web/templates/address_contract/index.html.eex:201 lib/block_scout_web/templates/address_contract/index.html.eex:213
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:19
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:13
msgid "Verify & Publish"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:301
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:52
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:89
msgid "Verify & publish"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:16
-#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:18 lib/block_scout_web/templates/transaction_log/_logs.html.eex:16
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:10
+#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:12 lib/block_scout_web/templates/transaction_log/_logs.html.eex:10
msgid "Verify the contract "
msgstr ""
@@ -2774,7 +2772,7 @@ msgid "View transaction %{transaction} on %{subnetwork}"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:130
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:124
msgid "WEI"
msgstr ""
@@ -2824,7 +2822,7 @@ msgid "Working Stake Amount"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:99
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:93
msgid "Write"
msgstr ""
@@ -2939,7 +2937,7 @@ msgid "custom RPC"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:43
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:37
msgid "fallback"
msgstr ""
@@ -2949,8 +2947,8 @@ msgid "false"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/address_logs/_logs.html.eex:16
-#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:18 lib/block_scout_web/templates/transaction_log/_logs.html.eex:16
+#: lib/block_scout_web/templates/address_logs/_logs.html.eex:10
+#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:12 lib/block_scout_web/templates/transaction_log/_logs.html.eex:10
msgid "here"
msgstr ""
@@ -2970,7 +2968,7 @@ msgid "of"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:21
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:15
msgid "page"
msgstr ""
@@ -2980,7 +2978,7 @@ msgid "pool owner"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:45
+#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:39
msgid "receive"
msgstr ""
@@ -3046,3 +3044,33 @@ msgstr ""
#: lib/block_scout_web/templates/block/overview.html.eex:209
msgid "burned from transactions included in the block (Base fee (per unit of gas) * Gas Used)."
msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:55
+msgid "Enter the Vyper Contract Code"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:18
+msgid "New Solidity Smart Contract Verification"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:13
+msgid "New Vyper Smart Contract Verification"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:41
+msgid "Via Sourcify: Sources and metadata JSON file"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:35
+msgid "Via flattened source code"
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:47
+msgid "Vyper contract"
+msgstr ""
diff --git a/apps/explorer/.gitignore b/apps/explorer/.gitignore
index bf75b19355..78db466aba 100644
--- a/apps/explorer/.gitignore
+++ b/apps/explorer/.gitignore
@@ -1,2 +1,3 @@
priv/.recovery
priv/solc_compilers/
+priv/vyper_compilers/
diff --git a/apps/explorer/.sobelow-conf b/apps/explorer/.sobelow-conf
index c97cb5e14b..c2b7ff895f 100644
--- a/apps/explorer/.sobelow-conf
+++ b/apps/explorer/.sobelow-conf
@@ -6,6 +6,7 @@
format: "compact",
ignore: ["Config.HTTPS"],
ignore_files: [
- "lib/explorer/smart_contract/solidity/code_compiler.ex"
+ "lib/explorer/smart_contract/solidity/code_compiler.ex",
+ "lib/explorer/smart_contract/vyper/code_compiler.ex"
]
]
diff --git a/apps/explorer/lib/explorer/application.ex b/apps/explorer/lib/explorer/application.ex
index b6ee12a58e..c295757a59 100644
--- a/apps/explorer/lib/explorer/application.ex
+++ b/apps/explorer/lib/explorer/application.ex
@@ -47,6 +47,7 @@ defmodule Explorer.Application do
Supervisor.child_spec({Task.Supervisor, name: Explorer.GenesisDataTaskSupervisor}, id: GenesisDataTaskSupervisor),
Supervisor.child_spec({Task.Supervisor, name: Explorer.TaskSupervisor}, id: Explorer.TaskSupervisor),
Explorer.SmartContract.SolcDownloader,
+ Explorer.SmartContract.VyperDownloader,
{Registry, keys: :duplicate, name: Registry.ChainEvents, id: Registry.ChainEvents},
{Admin.Recovery, [[], [name: Admin.Recovery]]},
TransactionCount,
diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex
index 66b0b86bf5..fd8a4feb09 100644
--- a/apps/explorer/lib/explorer/chain.ex
+++ b/apps/explorer/lib/explorer/chain.ex
@@ -3668,9 +3668,11 @@ defmodule Explorer.Chain do
creation_tx_query =
from(
tx in Transaction,
+ left_join: a in Address,
+ on: tx.created_contract_address_hash == a.hash,
where: tx.created_contract_address_hash == ^address_hash,
where: tx.status == ^1,
- select: tx.input
+ select: %{init: tx.input, created_contract_code: a.contract_code}
)
tx_input =
@@ -3678,7 +3680,9 @@ defmodule Explorer.Chain do
|> Repo.one()
if tx_input do
- Data.to_string(tx_input)
+ with %{init: input, created_contract_code: created_contract_code} <- tx_input do
+ %{init: Data.to_string(input), created_contract_code: Data.to_string(created_contract_code)}
+ end
else
creation_int_tx_query =
from(
@@ -3686,17 +3690,19 @@ defmodule Explorer.Chain do
join: t in assoc(itx, :transaction),
where: itx.created_contract_address_hash == ^address_hash,
where: t.status == ^1,
- select: itx.init
+ select: %{init: itx.init, created_contract_code: itx.created_contract_code}
)
- itx_init_code =
- creation_int_tx_query
- |> Repo.one()
+ res = creation_int_tx_query |> Repo.one()
- if itx_init_code do
- Data.to_string(itx_init_code)
- else
- nil
+ case res do
+ %{init: init, created_contract_code: created_contract_code} ->
+ init_str = Data.to_string(init)
+ created_contract_code_str = Data.to_string(created_contract_code)
+ %{init: init_str, created_contract_code: created_contract_code_str}
+
+ _ ->
+ nil
end
end
end
diff --git a/apps/explorer/lib/explorer/chain/smart_contract.ex b/apps/explorer/lib/explorer/chain/smart_contract.ex
index 0df4b02a54..f404178c9b 100644
--- a/apps/explorer/lib/explorer/chain/smart_contract.ex
+++ b/apps/explorer/lib/explorer/chain/smart_contract.ex
@@ -194,6 +194,7 @@ defmodule Explorer.Chain.SmartContract do
contract.
* `verified_via_sourcify` - whether contract verified through Sourcify utility or not.
* `partially_verified` - whether contract verified using partial matched source code or not.
+ * `is_vyper_contract` - boolean flag, determines if contract is Vyper or not
"""
@type t :: %Explorer.Chain.SmartContract{
@@ -207,7 +208,8 @@ defmodule Explorer.Chain.SmartContract do
abi: [function_description],
verified_via_sourcify: boolean | nil,
partially_verified: boolean | nil,
- file_path: String.t()
+ file_path: String.t(),
+ is_vyper_contract: boolean | nil
}
schema "smart_contracts" do
@@ -223,6 +225,7 @@ defmodule Explorer.Chain.SmartContract do
field(:verified_via_sourcify, :boolean)
field(:partially_verified, :boolean)
field(:file_path, :string)
+ field(:is_vyper_contract, :boolean)
has_many(
:decompiled_smart_contracts,
@@ -259,7 +262,8 @@ defmodule Explorer.Chain.SmartContract do
:optimization_runs,
:verified_via_sourcify,
:partially_verified,
- :file_path
+ :file_path,
+ :is_vyper_contract
])
|> validate_required([:name, :compiler_version, :optimization, :contract_source_code, :abi, :address_hash])
|> unique_constraint(:address_hash)
@@ -280,7 +284,8 @@ defmodule Explorer.Chain.SmartContract do
:constructor_arguments,
:verified_via_sourcify,
:partially_verified,
- :file_path
+ :file_path,
+ :is_vyper_contract
])
|> validate_required([:name, :compiler_version, :optimization, :address_hash])
diff --git a/apps/explorer/lib/explorer/smart_contract/compiler_version.ex b/apps/explorer/lib/explorer/smart_contract/compiler_version.ex
new file mode 100644
index 0000000000..d54522c63f
--- /dev/null
+++ b/apps/explorer/lib/explorer/smart_contract/compiler_version.ex
@@ -0,0 +1,197 @@
+defmodule Explorer.SmartContract.CompilerVersion do
+ @moduledoc """
+ Adapter for fetching compiler versions from https://solc-bin.ethereum.org/bin/list.json.
+ """
+
+ @unsupported_solc_versions ~w(0.1.1 0.1.2)
+ @unsupported_vyper_versions ~w(v0.2.9 v0.2.10)
+
+ @doc """
+ Fetches a list of compilers from the Ethereum Solidity API.
+ """
+ @spec fetch_versions(:solc | :vyper) :: {atom, [map]}
+ def fetch_versions(compiler) do
+ case compiler do
+ :solc -> fetch_solc_versions()
+ :vyper -> fetch_vyper_versions()
+ end
+ end
+
+ defp fetch_solc_versions do
+ headers = [{"Content-Type", "application/json"}]
+
+ case HTTPoison.get(source_url(:solc), headers) do
+ {:ok, %{status_code: 200, body: body}} ->
+ {:ok, format_data(body, :solc)}
+
+ {:ok, %{status_code: _status_code, body: body}} ->
+ {:error, decode_json(body)["error"]}
+
+ {:error, %{reason: reason}} ->
+ {:error, reason}
+ end
+ end
+
+ defp fetch_vyper_versions do
+ headers = [{"Content-Type", "application/json"}]
+
+ case HTTPoison.get(source_url(:vyper), headers) do
+ {:ok, %{status_code: 200, body: body}} ->
+ {:ok, format_data(body, :vyper)}
+
+ {:ok, %{status_code: _status_code, body: body}} ->
+ {:error, decode_json(body)["error"]}
+
+ {:error, %{reason: reason}} ->
+ {:error, reason}
+ end
+ end
+
+ @spec vyper_releases_url :: String.t()
+ def vyper_releases_url do
+ "https://api.github.com/repos/vyperlang/vyper/releases"
+ end
+
+ defp format_data(json, compiler) do
+ versions =
+ case compiler do
+ :solc ->
+ json
+ |> Jason.decode!()
+ |> Map.fetch!("builds")
+ |> remove_unsupported_versions(compiler)
+ |> format_versions(compiler)
+ |> Enum.reverse()
+
+ :vyper ->
+ json
+ |> Jason.decode!()
+ |> remove_unsupported_versions(compiler)
+ |> format_versions(compiler)
+ |> Enum.sort(fn version1, version2 ->
+ versions1 = String.split(version1, ".")
+ versions2 = String.split(version2, ".")
+ major1 = versions1 |> Enum.at(0) |> parse_integer()
+ major2 = versions2 |> Enum.at(0) |> parse_integer()
+ minor1 = versions1 |> Enum.at(1) |> parse_integer()
+ minor2 = versions2 |> Enum.at(1) |> parse_integer()
+ patch1 = versions1 |> Enum.at(2) |> String.split("-") |> Enum.at(0) |> parse_integer()
+ patch2 = versions2 |> Enum.at(2) |> String.split("-") |> Enum.at(0) |> parse_integer()
+ major1 >= major2 && minor1 >= minor2 && patch1 >= patch2
+ end)
+ end
+
+ ["latest" | versions]
+ end
+
+ defp parse_integer(string) do
+ case Integer.parse(string) do
+ {number, ""} -> number
+ _ -> nil
+ end
+ end
+
+ @spec remove_unsupported_versions([String.t()], :solc | :vyper) :: [String.t()]
+ defp remove_unsupported_versions(builds, compiler) do
+ case compiler do
+ :solc ->
+ Enum.reject(builds, fn %{"version" => version} ->
+ Enum.member?(@unsupported_solc_versions, version)
+ end)
+
+ :vyper ->
+ Enum.reject(builds, fn %{"tag_name" => version} ->
+ Enum.member?(@unsupported_vyper_versions, version)
+ end)
+ end
+ end
+
+ defp format_versions(builds, compiler) do
+ case compiler do
+ :solc ->
+ Enum.map(builds, fn build ->
+ build
+ |> Map.fetch!("path")
+ |> String.replace_prefix("soljson-", "")
+ |> String.replace_suffix(".js", "")
+ end)
+
+ :vyper ->
+ Enum.map(builds, fn build ->
+ build
+ |> Map.fetch!("tag_name")
+ end)
+ end
+ end
+
+ defp decode_json(json) do
+ Jason.decode!(json)
+ end
+
+ @spec source_url(:solc | :vyper) :: String.t()
+ defp source_url(compiler) do
+ case compiler do
+ :solc ->
+ solc_bin_api_url = Application.get_env(:explorer, :solc_bin_api_url)
+ "#{solc_bin_api_url}/bin/list.json"
+
+ :vyper ->
+ vyper_releases_url()
+ end
+ end
+
+ def get_strict_compiler_version(compiler, compiler_version) do
+ case compiler do
+ :solc ->
+ if compiler_version == "latest" do
+ compiler_versions = get_compiler_versions(:solc)
+
+ if Enum.count(compiler_versions) > 1 do
+ latest_stable_version =
+ compiler_versions
+ |> Enum.drop(1)
+ |> Enum.reduce_while("", fn version, acc ->
+ if String.contains?(version, "-nightly") do
+ {:cont, acc}
+ else
+ {:halt, version}
+ end
+ end)
+
+ latest_stable_version
+ else
+ "latest"
+ end
+ else
+ compiler_version
+ end
+
+ :vyper ->
+ if compiler_version == "latest" do
+ compiler_versions = get_compiler_versions(:vyper)
+
+ if Enum.count(compiler_versions) > 1 do
+ latest_stable_version =
+ compiler_versions
+ |> Enum.at(1)
+
+ latest_stable_version
+ else
+ "latest"
+ end
+ else
+ compiler_version
+ end
+ end
+ end
+
+ defp get_compiler_versions(compiler) do
+ case fetch_versions(compiler) do
+ {:ok, compiler_versions} ->
+ compiler_versions
+
+ {:error, _} ->
+ []
+ end
+ end
+end
diff --git a/apps/explorer/lib/explorer/smart_contract/solc_downloader.ex b/apps/explorer/lib/explorer/smart_contract/solc_downloader.ex
index 896dbe58e8..5f63b3189d 100644
--- a/apps/explorer/lib/explorer/smart_contract/solc_downloader.ex
+++ b/apps/explorer/lib/explorer/smart_contract/solc_downloader.ex
@@ -5,7 +5,7 @@ defmodule Explorer.SmartContract.SolcDownloader do
"""
use GenServer
- alias Explorer.SmartContract.Solidity.CompilerVersion
+ alias Explorer.SmartContract.CompilerVersion
@latest_compiler_refetch_time :timer.minutes(30)
@@ -16,7 +16,7 @@ defmodule Explorer.SmartContract.SolcDownloader do
path
else
compiler_versions =
- case CompilerVersion.fetch_versions() do
+ case CompilerVersion.fetch_versions(:solc) do
{:ok, compiler_versions} ->
compiler_versions
diff --git a/apps/explorer/lib/explorer/smart_contract/solidity/compiler_version.ex b/apps/explorer/lib/explorer/smart_contract/solidity/compiler_version.ex
deleted file mode 100644
index 91177e147a..0000000000
--- a/apps/explorer/lib/explorer/smart_contract/solidity/compiler_version.ex
+++ /dev/null
@@ -1,95 +0,0 @@
-defmodule Explorer.SmartContract.Solidity.CompilerVersion do
- @moduledoc """
- Adapter for fetching compiler versions from https://solc-bin.ethereum.org/bin/list.json.
- """
-
- @unsupported_versions ~w(0.1.1 0.1.2)
-
- @doc """
- Fetches a list of compilers from the Ethereum Solidity API.
- """
- @spec fetch_versions :: {atom, [map]}
- def fetch_versions do
- headers = [{"Content-Type", "application/json"}]
-
- case HTTPoison.get(source_url(), headers) do
- {:ok, %{status_code: 200, body: body}} ->
- {:ok, format_data(body)}
-
- {:ok, %{status_code: _status_code, body: body}} ->
- {:error, decode_json(body)["error"]}
-
- {:error, %{reason: reason}} ->
- {:error, reason}
- end
- end
-
- defp format_data(json) do
- versions =
- json
- |> Jason.decode!()
- |> Map.fetch!("builds")
- |> remove_unsupported_versions()
- |> format_versions()
- |> Enum.reverse()
-
- ["latest" | versions]
- end
-
- defp remove_unsupported_versions(builds) do
- Enum.reject(builds, fn %{"version" => version} ->
- Enum.member?(@unsupported_versions, version)
- end)
- end
-
- defp format_versions(builds) do
- Enum.map(builds, fn build ->
- build
- |> Map.fetch!("path")
- |> String.replace_prefix("soljson-", "")
- |> String.replace_suffix(".js", "")
- end)
- end
-
- defp decode_json(json) do
- Jason.decode!(json)
- end
-
- defp source_url do
- solc_bin_api_url = Application.get_env(:explorer, :solc_bin_api_url)
-
- "#{solc_bin_api_url}/bin/list.json"
- end
-
- def get_strict_compiler_version(compiler_version) do
- if compiler_version == "latest" do
- compiler_versions =
- case fetch_versions() do
- {:ok, compiler_versions} ->
- compiler_versions
-
- {:error, _} ->
- []
- end
-
- if Enum.count(compiler_versions) > 1 do
- latest_stable_version =
- compiler_versions
- |> Enum.drop(1)
- |> Enum.reduce_while("", fn version, acc ->
- if String.contains?(version, "-nightly") do
- {:cont, acc}
- else
- {:halt, version}
- end
- end)
-
- latest_stable_version
- else
- "latest"
- end
- else
- compiler_version
- end
- end
-end
diff --git a/apps/explorer/lib/explorer/smart_contract/publisher.ex b/apps/explorer/lib/explorer/smart_contract/solidity/publisher.ex
similarity index 93%
rename from apps/explorer/lib/explorer/smart_contract/publisher.ex
rename to apps/explorer/lib/explorer/smart_contract/solidity/publisher.ex
index 377fa9ed16..04066bb2af 100644
--- a/apps/explorer/lib/explorer/smart_contract/publisher.ex
+++ b/apps/explorer/lib/explorer/smart_contract/solidity/publisher.ex
@@ -1,18 +1,18 @@
-defmodule Explorer.SmartContract.Publisher do
+defmodule Explorer.SmartContract.Solidity.Publisher do
@moduledoc """
Module responsible to control the contract verification.
"""
alias Explorer.Chain
alias Explorer.Chain.SmartContract
- alias Explorer.SmartContract.Solidity.CompilerVersion
- alias Explorer.SmartContract.Verifier
+ alias Explorer.SmartContract.CompilerVersion
+ alias Explorer.SmartContract.Solidity.Verifier
@doc """
Evaluates smart contract authenticity and saves its details.
## Examples
- Explorer.SmartContract.Publisher.publish(
+ Explorer.SmartContract.Solidity.Publisher.publish(
"0x0f95fa9bc0383e699325f2658d04e8d96d87b90c",
%{
"compiler_version" => "0.4.24",
@@ -95,7 +95,7 @@ defmodule Explorer.SmartContract.Publisher do
prepared_external_libraries = prepare_external_libraies(params["external_libraries"])
- compiler_version = CompilerVersion.get_strict_compiler_version(params["compiler_version"])
+ compiler_version = CompilerVersion.get_strict_compiler_version(:solc, params["compiler_version"])
%{
address_hash: address_hash,
@@ -110,7 +110,8 @@ defmodule Explorer.SmartContract.Publisher do
secondary_sources: params["secondary_sources"],
abi: abi,
verified_via_sourcify: params["verified_via_sourcify"],
- partially_verified: params["partially_verified"]
+ partially_verified: params["partially_verified"],
+ is_vyper_contract: false
}
end
diff --git a/apps/explorer/lib/explorer/smart_contract/publisher_worker.ex b/apps/explorer/lib/explorer/smart_contract/solidity/publisher_worker.ex
similarity index 83%
rename from apps/explorer/lib/explorer/smart_contract/publisher_worker.ex
rename to apps/explorer/lib/explorer/smart_contract/solidity/publisher_worker.ex
index f0c248c8fb..0aaa2c768f 100644
--- a/apps/explorer/lib/explorer/smart_contract/publisher_worker.ex
+++ b/apps/explorer/lib/explorer/smart_contract/solidity/publisher_worker.ex
@@ -1,4 +1,4 @@
-defmodule Explorer.SmartContract.PublisherWorker do
+defmodule Explorer.SmartContract.Solidity.PublisherWorker do
@moduledoc """
Background smart contract verification worker.
"""
@@ -6,7 +6,7 @@ defmodule Explorer.SmartContract.PublisherWorker do
use Que.Worker, concurrency: 5
alias Explorer.Chain.Events.Publisher, as: EventsPublisher
- alias Explorer.SmartContract.Publisher
+ alias Explorer.SmartContract.Solidity.Publisher
def perform({address_hash, params, external_libraries, conn}) do
result =
diff --git a/apps/explorer/lib/explorer/smart_contract/verifier.ex b/apps/explorer/lib/explorer/smart_contract/solidity/verifier.ex
similarity index 97%
rename from apps/explorer/lib/explorer/smart_contract/verifier.ex
rename to apps/explorer/lib/explorer/smart_contract/solidity/verifier.ex
index 6153a4c5f9..09215391af 100644
--- a/apps/explorer/lib/explorer/smart_contract/verifier.ex
+++ b/apps/explorer/lib/explorer/smart_contract/solidity/verifier.ex
@@ -1,5 +1,5 @@
# credo:disable-for-this-file
-defmodule Explorer.SmartContract.Verifier do
+defmodule Explorer.SmartContract.Solidity.Verifier do
@moduledoc """
Module responsible to verify the Smart Contract.
@@ -107,12 +107,12 @@ defmodule Explorer.SmartContract.Verifier do
blockchain_created_tx_input =
case Chain.smart_contract_creation_tx_bytecode(address_hash) do
- nil ->
- bytecode
+ %{init: init, created_contract_code: _created_contract_code} ->
+ "0x" <> init_without_0x = init
+ init_without_0x
- blockchain_created_tx_input_with_0x ->
- "0x" <> blockchain_created_tx_input = blockchain_created_tx_input_with_0x
- blockchain_created_tx_input
+ _ ->
+ bytecode
end
%{
@@ -177,6 +177,10 @@ defmodule Explorer.SmartContract.Verifier do
For more information on the swarm hash, check out:
https://solidity.readthedocs.io/en/v0.5.3/metadata.html#encoding-of-the-metadata-hash-in-the-bytecode
"""
+ def extract_bytecode_and_metadata_hash(nil) do
+ %{"metadata_hash" => nil, "bytecode" => nil, "compiler_version" => nil}
+ end
+
def extract_bytecode_and_metadata_hash("0x" <> code) do
%{"metadata_hash" => metadata_hash, "bytecode" => bytecode, "compiler_version" => compiler_version} =
extract_bytecode_and_metadata_hash(code)
diff --git a/apps/explorer/lib/explorer/smart_contract/vyper/code_compiler.ex b/apps/explorer/lib/explorer/smart_contract/vyper/code_compiler.ex
new file mode 100644
index 0000000000..ae932cd52d
--- /dev/null
+++ b/apps/explorer/lib/explorer/smart_contract/vyper/code_compiler.ex
@@ -0,0 +1,74 @@
+defmodule Explorer.SmartContract.Vyper.CodeCompiler do
+ @moduledoc """
+ Module responsible to compile the Vyper code of a given Smart Contract.
+ """
+
+ alias Explorer.SmartContract.VyperDownloader
+
+ require Logger
+
+ @spec run(Keyword.t()) :: {:ok, map} | {:error, :compilation | :name}
+ def run(params) do
+ compiler_version = Keyword.fetch!(params, :compiler_version)
+ code = Keyword.fetch!(params, :code)
+
+ path = VyperDownloader.ensure_exists(compiler_version)
+
+ source_file_path = create_source_file(code)
+
+ if path do
+ {response, _status} =
+ System.cmd(
+ path,
+ [
+ "-f",
+ "abi,bytecode",
+ source_file_path
+ ]
+ )
+
+ response_data = String.split(response, "\n")
+ abi_row = response_data |> Enum.at(0)
+ bytecode = response_data |> Enum.at(1)
+
+ case Jason.decode(abi_row) do
+ {:ok, abi} ->
+ {:ok, %{"abi" => abi, "bytecode" => bytecode}}
+
+ {:error, %Jason.DecodeError{}} ->
+ {:error, :compilation}
+ end
+ else
+ {:error, :compilation}
+ end
+ end
+
+ def get_contract_info(contracts, _) when contracts == %{}, do: {:error, :compilation}
+
+ def get_contract_info(contracts, name) do
+ new_versions_name = ":" <> name
+
+ case contracts do
+ %{^new_versions_name => response} ->
+ response
+
+ %{^name => response} ->
+ response
+
+ _ ->
+ {:error, :name}
+ end
+ end
+
+ def parse_error({:error, %{"error" => error}}), do: {:error, [error]}
+ def parse_error({:error, %{"errors" => errors}}), do: {:error, errors}
+ def parse_error({:error, _} = error), do: error
+
+ defp create_source_file(source) do
+ {:ok, path} = Briefly.create()
+
+ File.write!(path, source)
+
+ path
+ end
+end
diff --git a/apps/explorer/lib/explorer/smart_contract/vyper/publisher.ex b/apps/explorer/lib/explorer/smart_contract/vyper/publisher.ex
new file mode 100644
index 0000000000..c22590e58c
--- /dev/null
+++ b/apps/explorer/lib/explorer/smart_contract/vyper/publisher.ex
@@ -0,0 +1,69 @@
+defmodule Explorer.SmartContract.Vyper.Publisher do
+ @moduledoc """
+ Module responsible to control Vyper contract verification.
+ """
+
+ alias Explorer.Chain
+ alias Explorer.Chain.SmartContract
+ alias Explorer.SmartContract.CompilerVersion
+ alias Explorer.SmartContract.Vyper.Verifier
+
+ def publish(address_hash, params) do
+ case Verifier.evaluate_authenticity(address_hash, params) do
+ {:ok, %{abi: abi}} ->
+ publish_smart_contract(address_hash, params, abi)
+
+ {:error, error} ->
+ {:error, unverified_smart_contract(address_hash, params, error, nil)}
+ end
+ end
+
+ def publish_smart_contract(address_hash, params, abi) do
+ attrs = address_hash |> attributes(params, abi)
+
+ Chain.create_smart_contract(attrs, attrs.external_libraries, attrs.secondary_sources)
+ end
+
+ defp unverified_smart_contract(address_hash, params, error, error_message) do
+ attrs = attributes(address_hash, params)
+
+ changeset =
+ SmartContract.invalid_contract_changeset(
+ %SmartContract{address_hash: address_hash},
+ attrs,
+ error,
+ error_message
+ )
+
+ %{changeset | action: :insert}
+ end
+
+ defp attributes(address_hash, params, abi \\ %{}) do
+ constructor_arguments = params["constructor_arguments"]
+
+ clean_constructor_arguments =
+ if constructor_arguments != nil && constructor_arguments != "" do
+ constructor_arguments
+ else
+ nil
+ end
+
+ compiler_version = CompilerVersion.get_strict_compiler_version(:vyper, params["compiler_version"])
+
+ %{
+ address_hash: address_hash,
+ name: "Vyper_contract",
+ compiler_version: compiler_version,
+ evm_version: nil,
+ optimization_runs: nil,
+ optimization: false,
+ contract_source_code: params["contract_source_code"],
+ constructor_arguments: clean_constructor_arguments,
+ external_libraries: [],
+ secondary_sources: [],
+ abi: abi,
+ verified_via_sourcify: false,
+ is_vyper_contract: true
+ }
+ end
+end
diff --git a/apps/explorer/lib/explorer/smart_contract/vyper/publisher_worker.ex b/apps/explorer/lib/explorer/smart_contract/vyper/publisher_worker.ex
new file mode 100644
index 0000000000..65053596cf
--- /dev/null
+++ b/apps/explorer/lib/explorer/smart_contract/vyper/publisher_worker.ex
@@ -0,0 +1,23 @@
+defmodule Explorer.SmartContract.Vyper.PublisherWorker do
+ @moduledoc """
+ Background smart contract verification worker.
+ """
+
+ use Que.Worker, concurrency: 5
+
+ alias Explorer.Chain.Events.Publisher, as: EventsPublisher
+ alias Explorer.SmartContract.Vyper.Publisher
+
+ def perform({address_hash, params, conn}) do
+ result =
+ case Publisher.publish(address_hash, params) do
+ {:ok, _contract} = result ->
+ result
+
+ {:error, changeset} ->
+ {:error, changeset}
+ end
+
+ EventsPublisher.broadcast([{:contract_verification_result, {address_hash, result, conn}}], :on_demand)
+ end
+end
diff --git a/apps/explorer/lib/explorer/smart_contract/vyper/verifier.ex b/apps/explorer/lib/explorer/smart_contract/vyper/verifier.ex
new file mode 100644
index 0000000000..1595223a0f
--- /dev/null
+++ b/apps/explorer/lib/explorer/smart_contract/vyper/verifier.ex
@@ -0,0 +1,65 @@
+# credo:disable-for-this-file
+defmodule Explorer.SmartContract.Vyper.Verifier do
+ @moduledoc """
+ Module responsible to verify the Smart Contract through Vyper.
+
+ Given a contract source code the bytecode will be generated and matched
+ against the existing Creation Address Bytecode, if it matches the contract is
+ then Verified.
+ """
+
+ alias Explorer.Chain
+ alias Explorer.SmartContract.Vyper.CodeCompiler
+
+ def evaluate_authenticity(_, %{"name" => ""}), do: {:error, :name}
+
+ def evaluate_authenticity(_, %{"contract_source_code" => ""}),
+ do: {:error, :contract_source_code}
+
+ def evaluate_authenticity(address_hash, params) do
+ verify(address_hash, params)
+ end
+
+ defp verify(address_hash, params) do
+ contract_source_code = Map.fetch!(params, "contract_source_code")
+ compiler_version = Map.fetch!(params, "compiler_version")
+ constructor_arguments = Map.get(params, "constructor_arguments", "")
+
+ vyper_output =
+ CodeCompiler.run(
+ compiler_version: compiler_version,
+ code: contract_source_code
+ )
+
+ compare_bytecodes(
+ vyper_output,
+ address_hash,
+ constructor_arguments
+ )
+ end
+
+ defp compare_bytecodes({:error, _}, _, _), do: {:error, :compilation}
+
+ # credo:disable-for-next-line /Complexity/
+ defp compare_bytecodes(
+ {:ok, %{"abi" => abi, "bytecode" => bytecode}},
+ address_hash,
+ arguments_data
+ ) do
+ blockchain_bytecode =
+ case Chain.smart_contract_creation_tx_bytecode(address_hash) do
+ %{init: init, created_contract_code: _created_contract_code} ->
+ init
+
+ _ ->
+ nil
+ end
+ |> String.trim()
+
+ if String.trim(bytecode <> arguments_data) == blockchain_bytecode do
+ {:ok, %{abi: abi}}
+ else
+ {:error, :generated_bytecode}
+ end
+ end
+end
diff --git a/apps/explorer/lib/explorer/smart_contract/vyper_downloader.ex b/apps/explorer/lib/explorer/smart_contract/vyper_downloader.ex
new file mode 100644
index 0000000000..dfb873276c
--- /dev/null
+++ b/apps/explorer/lib/explorer/smart_contract/vyper_downloader.ex
@@ -0,0 +1,127 @@
+defmodule Explorer.SmartContract.VyperDownloader do
+ @moduledoc """
+ Checks to see if the requested Vyper compiler version exists, and if not it
+ downloads and stores the file.
+ """
+ use GenServer
+
+ alias Explorer.SmartContract.CompilerVersion
+
+ @latest_compiler_refetch_time :timer.minutes(30)
+
+ def ensure_exists(version) do
+ path = file_path(version)
+
+ if File.exists?(path) && version !== "latest" do
+ path
+ else
+ compiler_versions =
+ case CompilerVersion.fetch_versions(:vyper) do
+ {:ok, compiler_versions} ->
+ compiler_versions
+
+ {:error, _} ->
+ []
+ end
+
+ if version in compiler_versions do
+ GenServer.call(__MODULE__, {:ensure_exists, version}, 60_000)
+ else
+ false
+ end
+ end
+ end
+
+ def start_link(_) do
+ GenServer.start_link(__MODULE__, [], name: __MODULE__)
+ end
+
+ # sobelow_skip ["Traversal"]
+ @impl true
+ def init([]) do
+ File.mkdir(compiler_dir())
+
+ {:ok, []}
+ end
+
+ # sobelow_skip ["Traversal"]
+ @impl true
+ def handle_call({:ensure_exists, version}, _from, state) do
+ path = file_path(version)
+
+ if fetch?(version, path) do
+ temp_path = file_path("#{version}-tmp")
+
+ contents = download(version)
+
+ file = File.open!(temp_path, [:write, :exclusive])
+
+ IO.binwrite(file, contents)
+ File.close(file)
+
+ File.rename(temp_path, path)
+ System.cmd("chmod", ["+x", path])
+ end
+
+ {:reply, path, state}
+ end
+
+ defp fetch?("latest", path) do
+ case File.stat(path) do
+ {:error, :enoent} ->
+ true
+
+ {:ok, %{mtime: mtime}} ->
+ last_modified = NaiveDateTime.from_erl!(mtime)
+ diff = Timex.diff(NaiveDateTime.utc_now(), last_modified, :milliseconds)
+
+ diff > @latest_compiler_refetch_time
+ end
+ end
+
+ defp fetch?(_, path) do
+ not File.exists?(path)
+ end
+
+ defp file_path(version) do
+ Path.join(compiler_dir(), "#{version}")
+ end
+
+ defp compiler_dir do
+ Application.app_dir(:explorer, "priv/vyper_compilers/")
+ end
+
+ defp download(version) do
+ version = CompilerVersion.get_strict_compiler_version(:vyper, version)
+ releases_path = CompilerVersion.vyper_releases_url()
+
+ releases_body =
+ releases_path
+ |> HTTPoison.get!([], timeout: 60_000, recv_timeout: 60_000)
+ |> Map.get(:body)
+ |> Jason.decode!()
+
+ release =
+ releases_body
+ |> Enum.find(fn release ->
+ Map.get(release, "tag_name") == version
+ end)
+
+ release_assets = Map.get(release, "assets")
+
+ download_path =
+ Enum.reduce_while(release_assets, "", fn asset, acc ->
+ browser_download_url = Map.get(asset, "browser_download_url")
+
+ if browser_download_url =~ "linux" do
+ {:halt, browser_download_url}
+ else
+ {:cont, acc}
+ end
+ end)
+
+ download_path
+ |> HTTPoison.get!([], timeout: 60_000, recv_timeout: 60_000, follow_redirect: true, hackney: [force_redirect: true])
+ |> Map.get(:body)
+ end
+end
diff --git a/apps/explorer/priv/repo/migrations/20210616120552_smart_contracts_add_is_vyper_flag.exs b/apps/explorer/priv/repo/migrations/20210616120552_smart_contracts_add_is_vyper_flag.exs
new file mode 100644
index 0000000000..171a90d4e4
--- /dev/null
+++ b/apps/explorer/priv/repo/migrations/20210616120552_smart_contracts_add_is_vyper_flag.exs
@@ -0,0 +1,9 @@
+defmodule Explorer.Repo.Migrations.SmartContractsAddIsVyperFlag do
+ use Ecto.Migration
+
+ def change do
+ alter table(:smart_contracts) do
+ add(:is_vyper_contract, :boolean, null: true)
+ end
+ end
+end
diff --git a/apps/explorer/test/explorer/smart_contract/solidity/compiler_version_test.exs b/apps/explorer/test/explorer/smart_contract/compiler_version_test.exs
similarity index 80%
rename from apps/explorer/test/explorer/smart_contract/solidity/compiler_version_test.exs
rename to apps/explorer/test/explorer/smart_contract/compiler_version_test.exs
index 6e36da7255..02e396d866 100644
--- a/apps/explorer/test/explorer/smart_contract/solidity/compiler_version_test.exs
+++ b/apps/explorer/test/explorer/smart_contract/compiler_version_test.exs
@@ -1,12 +1,12 @@
-defmodule Explorer.SmartContract.Solidity.CompilerVersionTest do
+defmodule Explorer.SmartContract.CompilerVersionTest do
use ExUnit.Case
- doctest Explorer.SmartContract.Solidity.CompilerVersion
+ doctest Explorer.SmartContract.CompilerVersion
- alias Explorer.SmartContract.Solidity.CompilerVersion
+ alias Explorer.SmartContract.CompilerVersion
alias Plug.Conn
- describe "fetch_versions" do
+ describe "fetch_versions/1" do
setup do
bypass = Bypass.open()
@@ -23,7 +23,7 @@ defmodule Explorer.SmartContract.Solidity.CompilerVersionTest do
Conn.resp(conn, 200, solc_bin_versions())
end)
- assert {:ok, versions} = CompilerVersion.fetch_versions()
+ assert {:ok, versions} = CompilerVersion.fetch_versions(:solc)
assert Enum.any?(versions, fn item -> item == "v0.4.9+commit.364da425" end) == true
end
@@ -35,7 +35,7 @@ defmodule Explorer.SmartContract.Solidity.CompilerVersionTest do
Conn.resp(conn, 200, solc_bin_versions())
end)
- assert {:ok, versions} = CompilerVersion.fetch_versions()
+ assert {:ok, versions} = CompilerVersion.fetch_versions(:solc)
assert List.first(versions) == "latest"
end
@@ -44,13 +44,13 @@ defmodule Explorer.SmartContract.Solidity.CompilerVersionTest do
Conn.resp(conn, 400, ~S({"error": "bad request"}))
end)
- assert {:error, "bad request"} = CompilerVersion.fetch_versions()
+ assert {:error, "bad request"} = CompilerVersion.fetch_versions(:solc)
end
test "returns error when there is server error", %{bypass: bypass} do
Bypass.down(bypass)
- assert {:error, :econnrefused} = CompilerVersion.fetch_versions()
+ assert {:error, :econnrefused} = CompilerVersion.fetch_versions(:solc)
end
end
diff --git a/apps/explorer/test/explorer/smart_contract/publisher_test.exs b/apps/explorer/test/explorer/smart_contract/solidity/publisher_test.exs
similarity index 98%
rename from apps/explorer/test/explorer/smart_contract/publisher_test.exs
rename to apps/explorer/test/explorer/smart_contract/solidity/publisher_test.exs
index 368c4d3a87..35ec6c3ca2 100644
--- a/apps/explorer/test/explorer/smart_contract/publisher_test.exs
+++ b/apps/explorer/test/explorer/smart_contract/solidity/publisher_test.exs
@@ -1,15 +1,15 @@
-defmodule Explorer.SmartContract.PublisherTest do
+defmodule Explorer.SmartContract.Solidity.PublisherTest do
use ExUnit.Case, async: true
use Explorer.DataCase
- doctest Explorer.SmartContract.Publisher
+ doctest Explorer.SmartContract.Solidity.Publisher
@moduletag timeout: :infinity
alias Explorer.Chain.{ContractMethod, SmartContract}
alias Explorer.{Factory, Repo}
- alias Explorer.SmartContract.Publisher
+ alias Explorer.SmartContract.Solidity.Publisher
describe "publish/2" do
test "with valid data creates a smart_contract" do
@@ -86,7 +86,7 @@ defmodule Explorer.SmartContract.PublisherTest do
}
response = Publisher.publish(contract_address.hash, valid_attrs)
- assert {:ok, %SmartContract{} = smart_contract} = response
+ assert {:ok, %SmartContract{} = _smart_contract} = response
Enum.each(contract_code_info.abi, fn selector ->
[parsed] = ABI.parse_specification([selector])
@@ -174,7 +174,7 @@ defmodule Explorer.SmartContract.PublisherTest do
end)
response = Publisher.publish(contract_address.hash, params, external_libraries_form_params)
- assert {:ok, %SmartContract{} = smart_contract} = response
+ assert {:ok, %SmartContract{} = _smart_contract} = response
end
end
end
diff --git a/apps/explorer/test/explorer/smart_contract/verifier_test.exs b/apps/explorer/test/explorer/smart_contract/solidity/verifier_test.exs
similarity index 99%
rename from apps/explorer/test/explorer/smart_contract/verifier_test.exs
rename to apps/explorer/test/explorer/smart_contract/solidity/verifier_test.exs
index ac170a6cf5..35029b3880 100644
--- a/apps/explorer/test/explorer/smart_contract/verifier_test.exs
+++ b/apps/explorer/test/explorer/smart_contract/solidity/verifier_test.exs
@@ -1,12 +1,12 @@
-defmodule Explorer.SmartContract.VerifierTest do
+defmodule Explorer.SmartContract.Solidity.VerifierTest do
use ExUnit.Case, async: true
use Explorer.DataCase
@moduletag timeout: :infinity
- doctest Explorer.SmartContract.Verifier
+ doctest Explorer.SmartContract.Solidity.Verifier
- alias Explorer.SmartContract.Verifier
+ alias Explorer.SmartContract.Solidity.Verifier
alias Explorer.Factory
@code_0_4 """
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 2ff694c011..961197c196 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -2,6 +2,17 @@ FROM bitwalker/alpine-elixir-phoenix:1.12
RUN apk --no-cache --update add alpine-sdk gmp-dev automake libtool inotify-tools autoconf python3 file
+ENV GLIBC_REPO=https://github.com/sgerrand/alpine-pkg-glibc
+ENV GLIBC_VERSION=2.30-r0
+
+RUN set -ex && \
+ apk --update add libstdc++ curl ca-certificates && \
+ for pkg in glibc-${GLIBC_VERSION} glibc-bin-${GLIBC_VERSION}; \
+ do curl -sSL ${GLIBC_REPO}/releases/download/${GLIBC_VERSION}/${pkg}.apk -o /tmp/${pkg}.apk; done && \
+ apk add --allow-untrusted /tmp/*.apk && \
+ rm -v /tmp/*.apk && \
+ /usr/glibc-compat/sbin/ldconfig /lib /usr/glibc-compat/lib
+
# Get Rust
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y