Merge pull request #4745 from blockscout/vb-premium-vyper-contracts-verification-rebased

Vyper contracts verification
pull/4747/head
Victor Baranov 3 years ago committed by GitHub
commit f08ad37603
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      .dialyzer-ignore
  2. 26
      .github/workflows/config.yml
  3. 1
      CHANGELOG.md
  4. 10
      apps/block_scout_web/assets/js/pages/verification_form.js
  5. 3
      apps/block_scout_web/assets/package-lock.json
  6. 21
      apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_controller.ex
  7. 4
      apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_via_flattened_code_controller.ex
  8. 47
      apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_vyper_controller.ex
  9. 46
      apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex
  10. 2
      apps/block_scout_web/lib/block_scout_web/controllers/api/v1/verified_smart_contract_controller.ex
  11. 73
      apps/block_scout_web/lib/block_scout_web/etherscan.ex
  12. 15
      apps/block_scout_web/lib/block_scout_web/notifier.ex
  13. 2
      apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex
  14. 27
      apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification/new.html.eex
  15. 2
      apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex
  16. 102
      apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex
  17. 8
      apps/block_scout_web/lib/block_scout_web/templates/address_logs/_logs.html.eex
  18. 1
      apps/block_scout_web/lib/block_scout_web/templates/layout/app.html.eex
  19. 8
      apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex
  20. 8
      apps/block_scout_web/lib/block_scout_web/templates/transaction/_decoded_input.html.eex
  21. 8
      apps/block_scout_web/lib/block_scout_web/templates/transaction_log/_logs.html.eex
  22. 5
      apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_vyper_view.ex
  23. 23
      apps/block_scout_web/lib/block_scout_web/web_router.ex
  24. 132
      apps/block_scout_web/priv/gettext/default.pot
  25. 132
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  26. 1
      apps/explorer/.gitignore
  27. 3
      apps/explorer/.sobelow-conf
  28. 1
      apps/explorer/lib/explorer/application.ex
  29. 26
      apps/explorer/lib/explorer/chain.ex
  30. 11
      apps/explorer/lib/explorer/chain/smart_contract.ex
  31. 197
      apps/explorer/lib/explorer/smart_contract/compiler_version.ex
  32. 4
      apps/explorer/lib/explorer/smart_contract/solc_downloader.ex
  33. 95
      apps/explorer/lib/explorer/smart_contract/solidity/compiler_version.ex
  34. 13
      apps/explorer/lib/explorer/smart_contract/solidity/publisher.ex
  35. 4
      apps/explorer/lib/explorer/smart_contract/solidity/publisher_worker.ex
  36. 16
      apps/explorer/lib/explorer/smart_contract/solidity/verifier.ex
  37. 74
      apps/explorer/lib/explorer/smart_contract/vyper/code_compiler.ex
  38. 69
      apps/explorer/lib/explorer/smart_contract/vyper/publisher.ex
  39. 23
      apps/explorer/lib/explorer/smart_contract/vyper/publisher_worker.ex
  40. 65
      apps/explorer/lib/explorer/smart_contract/vyper/verifier.ex
  41. 127
      apps/explorer/lib/explorer/smart_contract/vyper_downloader.ex
  42. 9
      apps/explorer/priv/repo/migrations/20210616120552_smart_contracts_add_is_vyper_flag.exs
  43. 16
      apps/explorer/test/explorer/smart_contract/compiler_version_test.exs
  44. 10
      apps/explorer/test/explorer/smart_contract/solidity/publisher_test.exs
  45. 6
      apps/explorer/test/explorer/smart_contract/solidity/verifier_test.exs
  46. 11
      docker/Dockerfile

@ -5,8 +5,10 @@
:0: Unknown type 'Elixir.Address':t/0 :0: Unknown type 'Elixir.Address':t/0
apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex:400: Function timestamp_to_datetime/1 has no local return 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/repo/prometheus_logger.ex:8
lib/explorer/smart_contract/publisher_worker.ex:1 lib/explorer/smart_contract/solidity/publisher_worker.ex:1
lib/explorer/smart_contract/publisher_worker.ex:6 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: 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() 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 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/indexer/fetcher/token_total_supply_on_demand.ex:16
lib/explorer/exchange_rates/source.ex:110 lib/explorer/exchange_rates/source.ex:110
lib/explorer/exchange_rates/source.ex:113 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:156
lib/block_scout_web/templates/address_contract/index.html.eex:199 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 lib/explorer/staking/stake_snapshotting.ex:15: Function do_snapshotting/7 has no local return

@ -38,7 +38,7 @@ jobs:
path: | path: |
deps deps
_build _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: | restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps- ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-
@ -98,7 +98,7 @@ jobs:
path: | path: |
deps deps
_build _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: | restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -122,7 +122,7 @@ jobs:
path: | path: |
deps deps
_build _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: | restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -145,7 +145,7 @@ jobs:
path: | path: |
deps deps
_build _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: | restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -154,7 +154,7 @@ jobs:
id: dialyzer-cache id: dialyzer-cache
with: with:
path: priv/plts 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: | restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-dialyzer-" ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-dialyzer-"
@ -185,7 +185,7 @@ jobs:
path: | path: |
deps deps
_build _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: | restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -211,7 +211,7 @@ jobs:
path: | path: |
deps deps
_build _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: | restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -239,7 +239,7 @@ jobs:
path: | path: |
deps deps
_build _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: | restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -288,7 +288,7 @@ jobs:
path: | path: |
deps deps
_build _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: | restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -350,7 +350,7 @@ jobs:
path: | path: |
deps deps
_build _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: | restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -406,7 +406,7 @@ jobs:
path: | path: |
deps deps
_build _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: | restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -473,7 +473,7 @@ jobs:
path: | path: |
deps deps
_build _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: | restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -534,7 +534,7 @@ jobs:
path: | path: |
deps deps
_build _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: | restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-" ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"

@ -1,6 +1,7 @@
## Current ## Current
### Features ### 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 - [#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 - [#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 - [#4660](https://github.com/blockscout/blockscout/pull/4660) - Save Sourcify path instead of filename

@ -233,6 +233,7 @@ if ($contractVerificationPage.length) {
if ($(this).prop('checked')) { if ($(this).prop('checked')) {
$('#verify_via_flattened_code_button').show() $('#verify_via_flattened_code_button').show()
$('#verify_via_sourcify_button').hide() $('#verify_via_sourcify_button').hide()
$('#verify_vyper_contract_button').hide()
} }
}) })
@ -240,6 +241,15 @@ if ($contractVerificationPage.length) {
if ($(this).prop('checked')) { if ($(this).prop('checked')) {
$('#verify_via_flattened_code_button').hide() $('#verify_via_flattened_code_button').hide()
$('#verify_via_sourcify_button').show() $('#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()
} }
}) })
} }

@ -77,7 +77,8 @@
}, },
"../../../deps/phoenix": { "../../../deps/phoenix": {
"version": "1.5.13", "version": "1.5.13",
"integrity": "sha512-aJKbnuCTtgH8qT7AzHhPwhOP3bqhja3ogFMQmUdY7jNzl/91nsUtpnz0M3HDW7j+IvfjiM2/TIkOaGwzW9BKhg==" "integrity": "sha512-aJKbnuCTtgH8qT7AzHhPwhOP3bqhja3ogFMQmUdY7jNzl/91nsUtpnz0M3HDW7j+IvfjiM2/TIkOaGwzW9BKhg==",
"license": "MIT"
}, },
"../../../deps/phoenix_html": { "../../../deps/phoenix_html": {
"version": "2.14.3", "version": "2.14.3",

@ -7,7 +7,9 @@ defmodule BlockScoutWeb.AddressContractVerificationController do
alias Explorer.Chain alias Explorer.Chain
alias Explorer.Chain.Events.Publisher, as: EventsPublisher alias Explorer.Chain.Events.Publisher, as: EventsPublisher
alias Explorer.Chain.SmartContract 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 alias Explorer.ThirdPartyIntegrations.Sourcify
def new(conn, %{"address_id" => address_hash_string}) do def new(conn, %{"address_id" => address_hash_string}) do
@ -26,7 +28,7 @@ defmodule BlockScoutWeb.AddressContractVerificationController do
) )
compiler_versions = compiler_versions =
case CompilerVersion.fetch_versions() do case CompilerVersion.fetch_versions(:solc) do
{:ok, compiler_versions} -> {:ok, compiler_versions} ->
compiler_versions compiler_versions
@ -50,7 +52,18 @@ defmodule BlockScoutWeb.AddressContractVerificationController do
"external_libraries" => external_libraries "external_libraries" => external_libraries
} }
) do ) 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, "") send_resp(conn, 204, "")
end end
@ -104,7 +117,7 @@ defmodule BlockScoutWeb.AddressContractVerificationController do
end end
def create(conn, _params) do def create(conn, _params) do
Que.add(PublisherWorker, {"", %{}, %{}, conn}) Que.add(SolidityPublisherWorker, {"", %{}, %{}, conn})
send_resp(conn, 204, "") send_resp(conn, 204, "")
end end

@ -4,7 +4,7 @@ defmodule BlockScoutWeb.AddressContractVerificationViaFlattenedCodeController do
alias BlockScoutWeb.Controller alias BlockScoutWeb.Controller
alias Explorer.Chain alias Explorer.Chain
alias Explorer.Chain.SmartContract 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 def new(conn, %{"address_id" => address_hash_string}) do
if Chain.smart_contract_fully_verified?(address_hash_string) do if Chain.smart_contract_fully_verified?(address_hash_string) do
@ -22,7 +22,7 @@ defmodule BlockScoutWeb.AddressContractVerificationViaFlattenedCodeController do
) )
compiler_versions = compiler_versions =
case CompilerVersion.fetch_versions() do case CompilerVersion.fetch_versions(:solc) do
{:ok, compiler_versions} -> {:ok, compiler_versions} ->
compiler_versions compiler_versions

@ -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

@ -6,7 +6,8 @@ defmodule BlockScoutWeb.API.RPC.ContractController do
alias Explorer.Chain alias Explorer.Chain
alias Explorer.Chain.Events.Publisher, as: EventsPublisher alias Explorer.Chain.Events.Publisher, as: EventsPublisher
alias Explorer.Chain.{Hash, SmartContract} 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 alias Explorer.ThirdPartyIntegrations.Sourcify
def verify(conn, %{"addressHash" => address_hash} = params) do def verify(conn, %{"addressHash" => address_hash} = params) do
@ -196,6 +197,40 @@ defmodule BlockScoutWeb.API.RPC.ContractController do
end end
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( def publish_without_broadcast(
%{"addressHash" => address_hash, "abi" => abi, "compilationTargetFilePath" => file_path} = input %{"addressHash" => address_hash, "abi" => abi, "compilationTargetFilePath" => file_path} = input
) do ) do
@ -416,6 +451,15 @@ defmodule BlockScoutWeb.API.RPC.ContractController do
|> parse_optimization_runs() |> parse_optimization_runs()
end 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 defp parse_optimization_runs({:ok, %{"optimization_runs" => runs} = opts}) when is_bitstring(runs) do
{:ok, Map.put(opts, "optimization_runs", 200)} {:ok, Map.put(opts, "optimization_runs", 200)}
end end

@ -3,7 +3,7 @@ defmodule BlockScoutWeb.API.V1.VerifiedSmartContractController do
alias Explorer.Chain alias Explorer.Chain
alias Explorer.Chain.Hash.Address alias Explorer.Chain.Hash.Address
alias Explorer.SmartContract.Publisher alias Explorer.SmartContract.Solidity.Publisher
def create(conn, params) do def create(conn, params) do
with {:ok, hash} <- validate_address_hash(params["address_hash"]), with {:ok, hash} <- validate_address_hash(params["address_hash"]),

@ -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.
<br/>
<br/>
<p class="api-doc-list-item-text">curl POST example:</p>
<br/>
<div class='tab-content'>
<div class='tab-pane fade show active'>
<div class="tile tile-muted p-1">
<div class="m-2">
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"'
</pre>
</div>
</div>
</div>
""",
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 %{ @contract_getabi_action %{
name: "getabi", name: "getabi",
description: "Get ABI for verified contract. Also available through a GraphQL 'addresses' query.", 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_getabi_action,
@contract_getsourcecode_action, @contract_getsourcecode_action,
@contract_verify_action, @contract_verify_action,
@contract_verify_via_sourcify_action @contract_verify_via_sourcify_action,
@contract_verify_vyper_contract_action
] ]
} }

@ -8,6 +8,7 @@ defmodule BlockScoutWeb.Notifier do
alias BlockScoutWeb.{ alias BlockScoutWeb.{
AddressContractVerificationViaFlattenedCodeView, AddressContractVerificationViaFlattenedCodeView,
AddressContractVerificationViaJsonView, AddressContractVerificationViaJsonView,
AddressContractVerificationVyperView,
Endpoint Endpoint
} }
@ -17,7 +18,7 @@ defmodule BlockScoutWeb.Notifier do
alias Explorer.Chain.Transaction.History.TransactionStats alias Explorer.Chain.Transaction.History.TransactionStats
alias Explorer.Counters.AverageBlockTime alias Explorer.Counters.AverageBlockTime
alias Explorer.ExchangeRates.Token alias Explorer.ExchangeRates.Token
alias Explorer.SmartContract.{Solidity.CodeCompiler, Solidity.CompilerVersion} alias Explorer.SmartContract.{CompilerVersion, Solidity.CodeCompiler}
alias Phoenix.View alias Phoenix.View
def handle_event({:chain_event, :addresses, type, addresses}) when type in [:realtime, :on_demand] do 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}} {:chain_event, :contract_verification_result, :on_demand, {address_hash, contract_verification_result, conn}}
) do ) do
verification_from_json_upload? = Map.has_key?(conn.params, "file") 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 = contract_verification_result =
case contract_verification_result do case contract_verification_result do
@ -55,7 +58,7 @@ defmodule BlockScoutWeb.Notifier do
{:error, changeset} -> {:error, changeset} ->
compiler_versions = compiler_versions =
case CompilerVersion.fetch_versions() do case CompilerVersion.fetch_versions(compiler) do
{:ok, compiler_versions} -> {:ok, compiler_versions} ->
compiler_versions compiler_versions
@ -64,10 +67,10 @@ defmodule BlockScoutWeb.Notifier do
end end
view = view =
if verification_from_json_upload? do cond do
AddressContractVerificationViaJsonView verification_from_json_upload? -> AddressContractVerificationViaJsonView
else verification_from_flattened_source? -> AddressContractVerificationViaFlattenedCodeView
AddressContractVerificationViaFlattenedCodeView true -> AddressContractVerificationVyperView
end end
result = result =

@ -51,7 +51,7 @@
<div class="d-none d-sm-block d-md-none"></br></br></div> <div class="d-none d-sm-block d-md-none"></br></br></div>
<div class="d-block d-sm-none"></br></br></div> <div class="d-block d-sm-none"></br></br></div>
<dt class="col-md-2 text-muted"><%= gettext "Optimization enabled" %></dt> <dt class="col-md-2 text-muted"><%= gettext "Optimization enabled" %></dt>
<dd class="col-md-4"><%= format_optimization_text(target_contract.optimization) %></dd> <dd class="col-md-4"><%= if target_contract.is_vyper_contract, do: "N/A", else: format_optimization_text(target_contract.optimization) %></dd>
</dl> </dl>
<dl class="row"> <dl class="row">
<dt class="col-md-2 text-muted"><%= gettext "Compiler version" %></dt> <dt class="col-md-2 text-muted"><%= gettext "Compiler version" %></dt>

@ -26,18 +26,25 @@
<div class="smart-contract-form-group"> <div class="smart-contract-form-group">
<div class="smart-contract-form-group-inner-wrapper"> <div class="smart-contract-form-group-inner-wrapper">
<%= label f, "Verification via" %> <%= label f, "Verify" %>
<div class="center-column"> <div class="center-column">
<div class="form-radios-group vertical"> <div class="form-radios-group vertical">
<div class="radio-big mb-3"> <div class="radio-big mb-3">
<%= radio_button f, :verify_via, true, checked: true, class: "form-check-input verify-via-flattened-code", "aria-describedby": "verify_via-help-block" %> <%= radio_button f, :verify_via, true, checked: true, class: "form-check-input verify-via-flattened-code", "aria-describedby": "verify_via-help-block" %>
<div class="radio-icon"></div> <div class="radio-icon"></div>
<%= label :verify_via, :true, gettext("Flattened source code"), class: "radio-text" %> <%= label :verify_via, :true, gettext("Via flattened source code"), class: "radio-text" %>
</div> </div>
<%= if Application.get_env(:explorer, Explorer.ThirdPartyIntegrations.Sourcify)[:enabled] do %>
<div class="radio-big">
<%= radio_button f, :verify_via, false, class: "form-check-input verify-via-sourcify" %>
<div class="radio-icon"></div>
<%= label :verify_via, :false, gettext("Via Sourcify: Sources and metadata JSON file"), class: "radio-text" %>
</div>
<% end %>
<div class="radio-big"> <div class="radio-big">
<%= radio_button f, :verify_via, false, class: "form-check-input verify-via-sourcify" %> <%= radio_button f, :verify_via, false, class: "form-check-input verify-vyper-contract" %>
<div class="radio-icon"></div> <div class="radio-icon"></div>
<%= label :verify_via, :false, gettext("Sourcify: Sources and metadata JSON file"), class: "radio-text" %> <%= label :verify_via, :false, gettext("Vyper contract"), class: "radio-text" %>
</div> </div>
</div> </div>
<%= error_tag f, :verify_via, id: "verify_via-help-block", class: "text-danger form-error" %> <%= error_tag f, :verify_via, id: "verify_via-help-block", class: "text-danger form-error" %>
@ -52,7 +59,9 @@
<i style="color: #f7b32b;" class="fa fa-info-circle ml-1" data-test="token-bridge-supply"></i></span><br/> <i style="color: #f7b32b;" class="fa fa-info-circle ml-1" data-test="token-bridge-supply"></i></span><br/>
2. Verification through <a href="https://sourcify.dev">Sourcify</a>.<br/> 2. Verification through <a href="https://sourcify.dev">Sourcify</a>.<br/>
a) if smart-contract already verified on Sourcify, it will automatically fetch the data from the <a href="https://repo.sourcify.dev">repo</a><br/> a) if smart-contract already verified on Sourcify, it will automatically fetch the data from the <a href="https://repo.sourcify.dev">repo</a><br/>
b) otherwise you will be asked to upload source files and JSON metadata file(s).</div> b) otherwise you will be asked to upload source files and JSON metadata file(s).<br/>
3. Verification of Vyper contract.
</div>
</div> </div>
</div> </div>
@ -86,6 +95,14 @@
style: "display: none;", style: "display: none;",
"data-button-loading": "animation" "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( link(
gettext("Cancel"), gettext("Cancel"),

@ -15,7 +15,7 @@
</div> </div>
<div class="new-smart-contract-form"> <div class="new-smart-contract-form">
<h1 class="smart-contract-title"><%= gettext "New Smart Contract Verification" %></h1> <h1 class="smart-contract-title"><%= gettext "New Solidity Smart Contract Verification" %></h1>
<%= form_for @changeset, <%= form_for @changeset,
address_contract_verification_path(@conn, :create), address_contract_verification_path(@conn, :create),

@ -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: "" %>
<section data-page="contract-verification" class="container new-smart-contract-container">
<div data-selector="channel-disconnected-message" class="d-none">
<div data-selector="reload-button" class="alert alert-danger">
<a href="#" class="alert-link"><%= gettext "Connection Lost" %></a>
</div>
</div>
<div class="new-smart-contract-form">
<h1 class="smart-contract-title"><%= gettext "New Vyper Smart Contract Verification" %></h1>
<%= form_for @changeset,
address_contract_verification_path(@conn, :create),
[],
fn f -> %>
<div class="smart-contract-form-group">
<div class="smart-contract-form-group-inner-wrapper">
<%= label f, :address_hash, gettext("Contract Address") %>
<div class="center-column">
<%= text_input f, :address_hash, class: "form-control border-rounded", "aria-describedby": "contract-address-help-block", readonly: true %>
<%= error_tag f, :address_hash, id: "contract-address-help-block", class: "text-danger form-error" %>
</div>
<div class="smart-contract-form-group-tooltip">The 0x address supplied on contract creation.</div>
</div>
</div>
<div class="smart-contract-form-group">
<div class="smart-contract-form-group-inner-wrapper">
<%= label f, :name, gettext("Contract Name") %>
<div class="center-column">
<%= text_input f, :name, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block", "data-test": "contract_name", value: contract_name_value, disabled: "true" %>
<%= error_tag f, :name, id: "contract-name-help-block", class: "text-danger form-error" %>
</div>
<div class="smart-contract-form-group-tooltip">Must match the name specified in the code.</div>
</div>
</div>
<div class="smart-contract-form-group">
<div class="smart-contract-form-group-inner-wrapper">
<%= label f, :compiler_version, gettext("Compiler") %>
<div class="center-column">
<%= select f, :compiler_version, @compiler_versions, class: "form-control border-rounded", selected: compiler_version, "aria-describedby": "compiler-help-block" %>
<%= error_tag f, :compiler_version, id: "compiler-help-block", class: "text-danger form-error" %>
</div>
<div class="smart-contract-form-group-tooltip"></div>
</div>
</div>
<div class="smart-contract-form-group">
<div class="smart-contract-form-group-inner-wrapper">
<%= label f, :contract_source_code, gettext("Enter the Vyper Contract Code") %>
<div class="center-column">
<%= textarea f, :contract_source_code, class: "form-control border-rounded monospace", rows: 3, "aria-describedby": "contract-source-code-help-block", value: contract_source_code_value %>
<%= error_tag f, :contract_source_code, id: "contract-source-code-help-block", class: "text-danger form-error", "data-test": "contract-source-code-error" %>
</div>
<div class="smart-contract-form-group-tooltip"></div>
</div>
</div>
<div class="smart-contract-form-group constructor-arguments" style="display: block">
<div class="smart-contract-form-group-inner-wrapper">
<%= label f, :constructor_arguments, gettext("ABI-encoded Constructor Arguments (if required by the contract)") %>
<div class="center-column">
<%= textarea f, :constructor_arguments, class: "form-control border-rounded monospace", rows: 3, "aria-describedby": "contract-constructor-arguments-help-block" %>
<%= error_tag f, :constructor_arguments, id: "contract-constructor-arguments-help-block", class: "text-danger form-error", "data-test": "contract-constructor-arguments-error" %>
</div>
<div class="smart-contract-form-group-tooltip">Add arguments in <a href="https://solidity.readthedocs.io/en/develop/abi-spec.html" target="_blank">ABI hex encoded form</a>. Constructor arguments are written right to left, and will be found at the end of the input created bytecode. They may also be <a href="https://abi.hashex.org/" target="_blank">parsed here.</a></div>
</div>
</div>
<div class="smart-contract-form-buttons">
<button
class="position-absolute w-118 btn-full-primary d-none mr-2"
disabled="true"
id="loading"
name="button"
type="button"
>
<span class="loading-spinner-small mr-2">
<span class="loading-spinner-block-1"></span>
<span class="loading-spinner-block-2"></span>
</span>
<%= gettext("Loading....") %>
</button>
<%= submit gettext("Verify & publish"), class: "btn-full-primary mr-2", "data-button-loading": "animation" %>
<%= reset gettext("Reset"), class: "btn-line mr-2 js-smart-contract-form-reset" %>
<%=
link(
gettext("Cancel"),
class: "btn-no-border",
to: address_contract_path(@conn, :index, @address_hash)
)
%>
</div>
<% end %>
</div>
<script defer data-cfasync="false" src="<%= static_path(@conn, "/js/verification-form.js") %>"></script>
</section>

@ -6,13 +6,7 @@
<%= gettext "To see accurate decoded input data, the contract must be verified." %> <%= gettext "To see accurate decoded input data, the contract must be verified." %>
<%= case @log.transaction do %> <%= case @log.transaction do %>
<% %{to_address: %{hash: hash}} -> %> <% %{to_address: %{hash: hash}} -> %>
<% path = <% path = address_verify_contract_path(@conn, :new, hash) %>
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
%>
<%= gettext "Verify the contract " %><a href="<%= path %>"><%= gettext "here" %></a> <%= gettext "Verify the contract " %><a href="<%= path %>"><%= gettext "here" %></a>
<% _ -> %> <% _ -> %>
<%= nil %> <%= nil %>

@ -219,6 +219,7 @@
@view_module != Elixir.BlockScoutWeb.AddressContractVerificationView && @view_module != Elixir.BlockScoutWeb.AddressContractVerificationView &&
@view_module != Elixir.BlockScoutWeb.AddressContractVerificationViaJsonView && @view_module != Elixir.BlockScoutWeb.AddressContractVerificationViaJsonView &&
@view_module != Elixir.BlockScoutWeb.AddressContractVerificationViaFlattenedCodeView && @view_module != Elixir.BlockScoutWeb.AddressContractVerificationViaFlattenedCodeView &&
@view_module != Elixir.BlockScoutWeb.AddressContractVerificationVyperView &&
@view_module != Elixir.BlockScoutWeb.AddressReadContractView && @view_module != Elixir.BlockScoutWeb.AddressReadContractView &&
@view_module != Elixir.BlockScoutWeb.AddressReadProxyView && @view_module != Elixir.BlockScoutWeb.AddressReadProxyView &&
@view_module != Elixir.BlockScoutWeb.AddressWriteContractView && @view_module != Elixir.BlockScoutWeb.AddressWriteContractView &&

@ -5,13 +5,7 @@
<%= if minimal_proxy_template do %> <%= if minimal_proxy_template do %>
<%= render BlockScoutWeb.CommonComponentsView, "_minimal_proxy_pattern.html", address_hash: metadata_for_verification.address_hash, conn: @conn %> <%= render BlockScoutWeb.CommonComponentsView, "_minimal_proxy_pattern.html", address_hash: metadata_for_verification.address_hash, conn: @conn %>
<% else %> <% else %>
<% path = <% path = address_verify_contract_path(@conn, :new, @address.hash) %>
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
%>
<div class="mb-4"> <div class="mb-4">
<%= render BlockScoutWeb.CommonComponentsView, "_info.html" %><span> <%= gettext("Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB") %> <%= link( <%= render BlockScoutWeb.CommonComponentsView, "_info.html" %><span> <%= gettext("Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB") %> <%= link(
metadata_for_verification.address_hash, metadata_for_verification.address_hash,

@ -8,13 +8,7 @@
<%= gettext "To see accurate decoded input data, the contract must be verified." %> <%= gettext "To see accurate decoded input data, the contract must be verified." %>
<%= case @transaction do %> <%= case @transaction do %>
<% %{to_address: %{hash: hash}} -> %> <% %{to_address: %{hash: hash}} -> %>
<% path = <% path = address_verify_contract_path(@conn, :new, hash) %>
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
%>
<%= gettext "Verify the contract " %><a href="<%= path %>"><%= gettext "here" %></a> <%= gettext "Verify the contract " %><a href="<%= path %>"><%= gettext "here" %></a>
<% _ -> %> <% _ -> %>
<%= nil %> <%= nil %>

@ -6,13 +6,7 @@
<%= gettext "To see accurate decoded input data, the contract must be verified." %> <%= gettext "To see accurate decoded input data, the contract must be verified." %>
<%= case @transaction do %> <%= case @transaction do %>
<% %{to_address: %{hash: hash}} -> %> <% %{to_address: %{hash: hash}} -> %>
<% path = <% path = address_verify_contract_path(@conn, :new, hash) %>
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
%>
<%= gettext "Verify the contract " %><a href="<%= path %>"><%= gettext "here" %></a> <%= gettext "Verify the contract " %><a href="<%= path %>"><%= gettext "here" %></a>
<% _ -> %> <% _ -> %>
<%= nil %> <%= nil %>

@ -0,0 +1,5 @@
defmodule BlockScoutWeb.AddressContractVerificationVyperView do
use BlockScoutWeb, :view
alias Explorer.Chain
end

@ -132,22 +132,6 @@ defmodule BlockScoutWeb.WebRouter do
as: :verify_contract 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( resources(
"/verify-via-flattened-code", "/verify-via-flattened-code",
AddressContractVerificationViaFlattenedCodeController, AddressContractVerificationViaFlattenedCodeController,
@ -162,6 +146,13 @@ defmodule BlockScoutWeb.WebRouter do
as: :verify_contract_via_json as: :verify_contract_via_json
) )
resources(
"/verify-vyper-contract",
AddressContractVerificationVyperController,
only: [:new],
as: :verify_vyper_contract
)
resources( resources(
"/read-contract", "/read-contract",
AddressReadContractController, AddressReadContractController,

@ -125,6 +125,7 @@ msgstr ""
#, elixir-format #, 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_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)" msgid "ABI-encoded Constructor Arguments (if required by the contract)"
msgstr "" msgstr ""
@ -170,7 +171,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:16 #: 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" msgid "Address"
msgstr "" msgstr ""
@ -199,7 +200,7 @@ msgid "All"
msgstr "" msgstr ""
#, elixir-format #, 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" msgid "All functions displayed below are from ABI of that contract. In order to verify current contract, proceed with"
msgstr "" msgstr ""
@ -434,9 +435,10 @@ msgid "Call Code"
msgstr "" msgstr ""
#, elixir-format #, 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_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 #: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:54
msgid "Cancel" msgid "Cancel"
msgstr "" msgstr ""
@ -526,6 +528,7 @@ msgstr ""
#, elixir-format #, 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_via_flattened_code/new.html.eex:71
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:44
msgid "Compiler" msgid "Compiler"
msgstr "" msgstr ""
@ -552,7 +555,8 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:4 #: 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_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" msgid "Connection Lost"
msgstr "" msgstr ""
@ -596,7 +600,8 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:18 #: 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_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" msgid "Contract Address"
msgstr "" msgstr ""
@ -629,12 +634,13 @@ msgstr ""
#, elixir-format #, 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_via_flattened_code/new.html.eex:38
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:33
msgid "Contract Name" msgid "Contract Name"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:22 #: 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" msgid "Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB"
msgstr "" msgstr ""
@ -817,9 +823,9 @@ msgid "DApp for Staking %{symbol} tokens"
msgstr "" msgstr ""
#, elixir-format #, 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/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" msgid "Data"
msgstr "" msgstr ""
@ -840,10 +846,10 @@ msgid "Decimals"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:38 #: lib/block_scout_web/templates/address_logs/_logs.html.eex:32
#: 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/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:39 lib/block_scout_web/templates/transaction_log/_logs.html.eex:47 #: 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:62 #: lib/block_scout_web/templates/transaction_log/_logs.html.eex:56
msgid "Decoded" msgid "Decoded"
msgstr "" msgstr ""
@ -943,8 +949,8 @@ msgid "ERC-721 "
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:95 #: lib/block_scout_web/templates/smart_contract/_functions.html.eex:89
#: 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:125
msgid "ETH" msgid "ETH"
msgstr "" msgstr ""
@ -1047,13 +1053,13 @@ msgid "External libraries"
msgstr "" msgstr ""
#, elixir-format #, 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." msgid "Failed to decode input data."
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:41 #: lib/block_scout_web/templates/address_logs/_logs.html.eex:35
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:42 #: lib/block_scout_web/templates/transaction_log/_logs.html.eex:36
msgid "Failed to decode log data." msgid "Failed to decode log data."
msgstr "" msgstr ""
@ -1067,11 +1073,6 @@ msgstr ""
msgid "Fetching tokens..." msgid "Fetching tokens..."
msgstr "" msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:35
msgid "Flattened source code"
msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/admin/dashboard/index.html.eex:16 #: 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." 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 "" msgstr ""
#, elixir-format #, 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." msgid "IMPORTANT: This information is a best guess based on similar functions from other verified contracts."
msgstr "" msgstr ""
@ -1340,9 +1341,10 @@ msgid "Loading..."
msgstr "" msgstr ""
#, elixir-format #, 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_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_via_json/new.html.eex:50
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:87
msgid "Loading...." msgid "Loading...."
msgstr "" msgstr ""
@ -1352,7 +1354,7 @@ msgid "Log Data"
msgstr "" msgstr ""
#, elixir-format #, 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" msgid "Log Index"
msgstr "" msgstr ""
@ -1512,14 +1514,13 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:9 #: 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 #: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:9
msgid "New Smart Contract Verification" msgid "New Smart Contract Verification"
msgstr "" msgstr ""
#, elixir-format #, 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:84
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:82 #: 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" msgid "Next"
msgstr "" msgstr ""
@ -1672,7 +1673,7 @@ msgid "Potential Reward Share"
msgstr "" msgstr ""
#, elixir-format #, 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:" msgid "Potential matches from our contract method database:"
msgstr "" msgstr ""
@ -1710,7 +1711,7 @@ msgid "QR Code"
msgstr "" msgstr ""
#, elixir-format #, 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" msgid "Query"
msgstr "" msgstr ""
@ -1776,6 +1777,7 @@ msgstr ""
#, elixir-format #, 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_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_via_json/new.html.eex:53
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:90
msgid "Reset" msgid "Reset"
msgstr "" msgstr ""
@ -1944,11 +1946,6 @@ msgstr ""
msgid "Sources and Metadata JSON" msgid "Sources and Metadata JSON"
msgstr "" 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 #, elixir-format
#: lib/block_scout_web/templates/stakes/_stakes_modal_stake.html.eex:7 #: lib/block_scout_web/templates/stakes/_stakes_modal_stake.html.eex:7
msgid "Stake" msgid "Stake"
@ -2089,7 +2086,7 @@ msgid "The block height of a particular block is defined as the number of blocks
msgstr "" msgstr ""
#, elixir-format #, 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." 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 "" msgstr ""
@ -2129,7 +2126,7 @@ msgid "The percentage of stake in a single pool relative to the total amount sta
msgstr "" msgstr ""
#, elixir-format #, 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." 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 "" msgstr ""
@ -2330,7 +2327,7 @@ msgid "To"
msgstr "" msgstr ""
#, elixir-format #, 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." msgid "To have guaranteed accuracy, use the link above to verify the contract's source code."
msgstr "" msgstr ""
@ -2436,8 +2433,8 @@ msgid "Topic"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:77 #: lib/block_scout_web/templates/address_logs/_logs.html.eex:71
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:80 #: lib/block_scout_web/templates/transaction_log/_logs.html.eex:74
msgid "Topics" msgid "Topics"
msgstr "" msgstr ""
@ -2482,7 +2479,7 @@ msgid "Trade STAKE on BitMax"
msgstr "" msgstr ""
#, elixir-format #, 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 #: lib/block_scout_web/views/transaction_view.ex:463
msgid "Transaction" msgid "Transaction"
msgstr "" 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: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: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/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" msgid "Verify & Publish"
msgstr "" msgstr ""
#, elixir-format #, 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_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_via_json/new.html.eex:52
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:89
msgid "Verify & publish" msgid "Verify & publish"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/_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:18 lib/block_scout_web/templates/transaction_log/_logs.html.eex:16 #: 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 " msgid "Verify the contract "
msgstr "" msgstr ""
@ -2774,7 +2772,7 @@ msgid "View transaction %{transaction} on %{subnetwork}"
msgstr "" msgstr ""
#, elixir-format #, 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" msgid "WEI"
msgstr "" msgstr ""
@ -2824,7 +2822,7 @@ msgid "Working Stake Amount"
msgstr "" msgstr ""
#, elixir-format #, 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" msgid "Write"
msgstr "" msgstr ""
@ -2939,7 +2937,7 @@ msgid "custom RPC"
msgstr "" msgstr ""
#, elixir-format #, 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" msgid "fallback"
msgstr "" msgstr ""
@ -2949,8 +2947,8 @@ msgid "false"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/_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:18 lib/block_scout_web/templates/transaction_log/_logs.html.eex:16 #: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:12 lib/block_scout_web/templates/transaction_log/_logs.html.eex:10
msgid "here" msgid "here"
msgstr "" msgstr ""
@ -2970,7 +2968,7 @@ msgid "of"
msgstr "" msgstr ""
#, elixir-format #, 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" msgid "page"
msgstr "" msgstr ""
@ -2980,7 +2978,7 @@ msgid "pool owner"
msgstr "" msgstr ""
#, elixir-format #, 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" msgid "receive"
msgstr "" msgstr ""
@ -3046,3 +3044,33 @@ msgstr ""
#: lib/block_scout_web/templates/block/overview.html.eex:209 #: 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)." msgid "burned from transactions included in the block (Base fee (per unit of gas) * Gas Used)."
msgstr "" 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 ""

@ -125,6 +125,7 @@ msgstr ""
#, elixir-format #, 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_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)" msgid "ABI-encoded Constructor Arguments (if required by the contract)"
msgstr "" msgstr ""
@ -170,7 +171,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:16 #: 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" msgid "Address"
msgstr "" msgstr ""
@ -199,7 +200,7 @@ msgid "All"
msgstr "" msgstr ""
#, elixir-format #, 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" msgid "All functions displayed below are from ABI of that contract. In order to verify current contract, proceed with"
msgstr "" msgstr ""
@ -434,9 +435,10 @@ msgid "Call Code"
msgstr "" msgstr ""
#, elixir-format #, 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_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 #: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:54
msgid "Cancel" msgid "Cancel"
msgstr "" msgstr ""
@ -526,6 +528,7 @@ msgstr ""
#, elixir-format #, 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_via_flattened_code/new.html.eex:71
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:44
msgid "Compiler" msgid "Compiler"
msgstr "" msgstr ""
@ -552,7 +555,8 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:4 #: 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_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" msgid "Connection Lost"
msgstr "" msgstr ""
@ -596,7 +600,8 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:18 #: 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_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" msgid "Contract Address"
msgstr "" msgstr ""
@ -629,12 +634,13 @@ msgstr ""
#, elixir-format #, 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_via_flattened_code/new.html.eex:38
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:33
msgid "Contract Name" msgid "Contract Name"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:22 #: 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" msgid "Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB"
msgstr "" msgstr ""
@ -817,9 +823,9 @@ msgid "DApp for Staking %{symbol} tokens"
msgstr "" msgstr ""
#, elixir-format #, 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/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" msgid "Data"
msgstr "" msgstr ""
@ -840,10 +846,10 @@ msgid "Decimals"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:38 #: lib/block_scout_web/templates/address_logs/_logs.html.eex:32
#: 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/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:39 lib/block_scout_web/templates/transaction_log/_logs.html.eex:47 #: 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:62 #: lib/block_scout_web/templates/transaction_log/_logs.html.eex:56
msgid "Decoded" msgid "Decoded"
msgstr "" msgstr ""
@ -943,8 +949,8 @@ msgid "ERC-721 "
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:95 #: lib/block_scout_web/templates/smart_contract/_functions.html.eex:89
#: 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:125
msgid "ETH" msgid "ETH"
msgstr "" msgstr ""
@ -1047,13 +1053,13 @@ msgid "External libraries"
msgstr "" msgstr ""
#, elixir-format #, 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." msgid "Failed to decode input data."
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:41 #: lib/block_scout_web/templates/address_logs/_logs.html.eex:35
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:42 #: lib/block_scout_web/templates/transaction_log/_logs.html.eex:36
msgid "Failed to decode log data." msgid "Failed to decode log data."
msgstr "" msgstr ""
@ -1067,11 +1073,6 @@ msgstr ""
msgid "Fetching tokens..." msgid "Fetching tokens..."
msgstr "" msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:35
msgid "Flattened source code"
msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/admin/dashboard/index.html.eex:16 #: 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." 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 "" msgstr ""
#, elixir-format #, 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." msgid "IMPORTANT: This information is a best guess based on similar functions from other verified contracts."
msgstr "" msgstr ""
@ -1340,9 +1341,10 @@ msgid "Loading..."
msgstr "" msgstr ""
#, elixir-format #, 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_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_via_json/new.html.eex:50
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:87
msgid "Loading...." msgid "Loading...."
msgstr "" msgstr ""
@ -1352,7 +1354,7 @@ msgid "Log Data"
msgstr "" msgstr ""
#, elixir-format #, 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" msgid "Log Index"
msgstr "" msgstr ""
@ -1512,14 +1514,13 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:9 #: 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 #: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:9
msgid "New Smart Contract Verification" msgid "New Smart Contract Verification"
msgstr "" msgstr ""
#, elixir-format #, 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:84
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:82 #: 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" msgid "Next"
msgstr "" msgstr ""
@ -1672,7 +1673,7 @@ msgid "Potential Reward Share"
msgstr "" msgstr ""
#, elixir-format #, 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:" msgid "Potential matches from our contract method database:"
msgstr "" msgstr ""
@ -1710,7 +1711,7 @@ msgid "QR Code"
msgstr "" msgstr ""
#, elixir-format #, 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" msgid "Query"
msgstr "" msgstr ""
@ -1776,6 +1777,7 @@ msgstr ""
#, elixir-format #, 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_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_via_json/new.html.eex:53
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:90
msgid "Reset" msgid "Reset"
msgstr "" msgstr ""
@ -1944,11 +1946,6 @@ msgstr ""
msgid "Sources and Metadata JSON" msgid "Sources and Metadata JSON"
msgstr "" 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 #, elixir-format
#: lib/block_scout_web/templates/stakes/_stakes_modal_stake.html.eex:7 #: lib/block_scout_web/templates/stakes/_stakes_modal_stake.html.eex:7
msgid "Stake" msgid "Stake"
@ -2089,7 +2086,7 @@ msgid "The block height of a particular block is defined as the number of blocks
msgstr "" msgstr ""
#, elixir-format #, 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." 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 "" msgstr ""
@ -2129,7 +2126,7 @@ msgid "The percentage of stake in a single pool relative to the total amount sta
msgstr "" msgstr ""
#, elixir-format #, 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." 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 "" msgstr ""
@ -2330,7 +2327,7 @@ msgid "To"
msgstr "" msgstr ""
#, elixir-format #, 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." msgid "To have guaranteed accuracy, use the link above to verify the contract's source code."
msgstr "" msgstr ""
@ -2436,8 +2433,8 @@ msgid "Topic"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:77 #: lib/block_scout_web/templates/address_logs/_logs.html.eex:71
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:80 #: lib/block_scout_web/templates/transaction_log/_logs.html.eex:74
msgid "Topics" msgid "Topics"
msgstr "" msgstr ""
@ -2482,7 +2479,7 @@ msgid "Trade STAKE on BitMax"
msgstr "" msgstr ""
#, elixir-format #, 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 #: lib/block_scout_web/views/transaction_view.ex:463
msgid "Transaction" msgid "Transaction"
msgstr "" 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: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: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/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" msgid "Verify & Publish"
msgstr "" msgstr ""
#, elixir-format #, 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_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_via_json/new.html.eex:52
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:89
msgid "Verify & publish" msgid "Verify & publish"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/_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:18 lib/block_scout_web/templates/transaction_log/_logs.html.eex:16 #: 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 " msgid "Verify the contract "
msgstr "" msgstr ""
@ -2774,7 +2772,7 @@ msgid "View transaction %{transaction} on %{subnetwork}"
msgstr "" msgstr ""
#, elixir-format #, 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" msgid "WEI"
msgstr "" msgstr ""
@ -2824,7 +2822,7 @@ msgid "Working Stake Amount"
msgstr "" msgstr ""
#, elixir-format #, 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" msgid "Write"
msgstr "" msgstr ""
@ -2939,7 +2937,7 @@ msgid "custom RPC"
msgstr "" msgstr ""
#, elixir-format #, 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" msgid "fallback"
msgstr "" msgstr ""
@ -2949,8 +2947,8 @@ msgid "false"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address_logs/_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:18 lib/block_scout_web/templates/transaction_log/_logs.html.eex:16 #: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:12 lib/block_scout_web/templates/transaction_log/_logs.html.eex:10
msgid "here" msgid "here"
msgstr "" msgstr ""
@ -2970,7 +2968,7 @@ msgid "of"
msgstr "" msgstr ""
#, elixir-format #, 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" msgid "page"
msgstr "" msgstr ""
@ -2980,7 +2978,7 @@ msgid "pool owner"
msgstr "" msgstr ""
#, elixir-format #, 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" msgid "receive"
msgstr "" msgstr ""
@ -3046,3 +3044,33 @@ msgstr ""
#: lib/block_scout_web/templates/block/overview.html.eex:209 #: 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)." msgid "burned from transactions included in the block (Base fee (per unit of gas) * Gas Used)."
msgstr "" 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 ""

@ -1,2 +1,3 @@
priv/.recovery priv/.recovery
priv/solc_compilers/ priv/solc_compilers/
priv/vyper_compilers/

@ -6,6 +6,7 @@
format: "compact", format: "compact",
ignore: ["Config.HTTPS"], ignore: ["Config.HTTPS"],
ignore_files: [ 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"
] ]
] ]

@ -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.GenesisDataTaskSupervisor}, id: GenesisDataTaskSupervisor),
Supervisor.child_spec({Task.Supervisor, name: Explorer.TaskSupervisor}, id: Explorer.TaskSupervisor), Supervisor.child_spec({Task.Supervisor, name: Explorer.TaskSupervisor}, id: Explorer.TaskSupervisor),
Explorer.SmartContract.SolcDownloader, Explorer.SmartContract.SolcDownloader,
Explorer.SmartContract.VyperDownloader,
{Registry, keys: :duplicate, name: Registry.ChainEvents, id: Registry.ChainEvents}, {Registry, keys: :duplicate, name: Registry.ChainEvents, id: Registry.ChainEvents},
{Admin.Recovery, [[], [name: Admin.Recovery]]}, {Admin.Recovery, [[], [name: Admin.Recovery]]},
TransactionCount, TransactionCount,

@ -3668,9 +3668,11 @@ defmodule Explorer.Chain do
creation_tx_query = creation_tx_query =
from( from(
tx in Transaction, 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.created_contract_address_hash == ^address_hash,
where: tx.status == ^1, where: tx.status == ^1,
select: tx.input select: %{init: tx.input, created_contract_code: a.contract_code}
) )
tx_input = tx_input =
@ -3678,7 +3680,9 @@ defmodule Explorer.Chain do
|> Repo.one() |> Repo.one()
if tx_input do 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 else
creation_int_tx_query = creation_int_tx_query =
from( from(
@ -3686,17 +3690,19 @@ defmodule Explorer.Chain do
join: t in assoc(itx, :transaction), join: t in assoc(itx, :transaction),
where: itx.created_contract_address_hash == ^address_hash, where: itx.created_contract_address_hash == ^address_hash,
where: t.status == ^1, where: t.status == ^1,
select: itx.init select: %{init: itx.init, created_contract_code: itx.created_contract_code}
) )
itx_init_code = res = creation_int_tx_query |> Repo.one()
creation_int_tx_query
|> Repo.one()
if itx_init_code do case res do
Data.to_string(itx_init_code) %{init: init, created_contract_code: created_contract_code} ->
else init_str = Data.to_string(init)
nil created_contract_code_str = Data.to_string(created_contract_code)
%{init: init_str, created_contract_code: created_contract_code_str}
_ ->
nil
end end
end end
end end

@ -194,6 +194,7 @@ defmodule Explorer.Chain.SmartContract do
contract. contract.
* `verified_via_sourcify` - whether contract verified through Sourcify utility or not. * `verified_via_sourcify` - whether contract verified through Sourcify utility or not.
* `partially_verified` - whether contract verified using partial matched source code 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{ @type t :: %Explorer.Chain.SmartContract{
@ -207,7 +208,8 @@ defmodule Explorer.Chain.SmartContract do
abi: [function_description], abi: [function_description],
verified_via_sourcify: boolean | nil, verified_via_sourcify: boolean | nil,
partially_verified: boolean | nil, partially_verified: boolean | nil,
file_path: String.t() file_path: String.t(),
is_vyper_contract: boolean | nil
} }
schema "smart_contracts" do schema "smart_contracts" do
@ -223,6 +225,7 @@ defmodule Explorer.Chain.SmartContract do
field(:verified_via_sourcify, :boolean) field(:verified_via_sourcify, :boolean)
field(:partially_verified, :boolean) field(:partially_verified, :boolean)
field(:file_path, :string) field(:file_path, :string)
field(:is_vyper_contract, :boolean)
has_many( has_many(
:decompiled_smart_contracts, :decompiled_smart_contracts,
@ -259,7 +262,8 @@ defmodule Explorer.Chain.SmartContract do
:optimization_runs, :optimization_runs,
:verified_via_sourcify, :verified_via_sourcify,
:partially_verified, :partially_verified,
:file_path :file_path,
:is_vyper_contract
]) ])
|> validate_required([:name, :compiler_version, :optimization, :contract_source_code, :abi, :address_hash]) |> validate_required([:name, :compiler_version, :optimization, :contract_source_code, :abi, :address_hash])
|> unique_constraint(:address_hash) |> unique_constraint(:address_hash)
@ -280,7 +284,8 @@ defmodule Explorer.Chain.SmartContract do
:constructor_arguments, :constructor_arguments,
:verified_via_sourcify, :verified_via_sourcify,
:partially_verified, :partially_verified,
:file_path :file_path,
:is_vyper_contract
]) ])
|> validate_required([:name, :compiler_version, :optimization, :address_hash]) |> validate_required([:name, :compiler_version, :optimization, :address_hash])

@ -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

@ -5,7 +5,7 @@ defmodule Explorer.SmartContract.SolcDownloader do
""" """
use GenServer use GenServer
alias Explorer.SmartContract.Solidity.CompilerVersion alias Explorer.SmartContract.CompilerVersion
@latest_compiler_refetch_time :timer.minutes(30) @latest_compiler_refetch_time :timer.minutes(30)
@ -16,7 +16,7 @@ defmodule Explorer.SmartContract.SolcDownloader do
path path
else else
compiler_versions = compiler_versions =
case CompilerVersion.fetch_versions() do case CompilerVersion.fetch_versions(:solc) do
{:ok, compiler_versions} -> {:ok, compiler_versions} ->
compiler_versions compiler_versions

@ -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

@ -1,18 +1,18 @@
defmodule Explorer.SmartContract.Publisher do defmodule Explorer.SmartContract.Solidity.Publisher do
@moduledoc """ @moduledoc """
Module responsible to control the contract verification. Module responsible to control the contract verification.
""" """
alias Explorer.Chain alias Explorer.Chain
alias Explorer.Chain.SmartContract alias Explorer.Chain.SmartContract
alias Explorer.SmartContract.Solidity.CompilerVersion alias Explorer.SmartContract.CompilerVersion
alias Explorer.SmartContract.Verifier alias Explorer.SmartContract.Solidity.Verifier
@doc """ @doc """
Evaluates smart contract authenticity and saves its details. Evaluates smart contract authenticity and saves its details.
## Examples ## Examples
Explorer.SmartContract.Publisher.publish( Explorer.SmartContract.Solidity.Publisher.publish(
"0x0f95fa9bc0383e699325f2658d04e8d96d87b90c", "0x0f95fa9bc0383e699325f2658d04e8d96d87b90c",
%{ %{
"compiler_version" => "0.4.24", "compiler_version" => "0.4.24",
@ -95,7 +95,7 @@ defmodule Explorer.SmartContract.Publisher do
prepared_external_libraries = prepare_external_libraies(params["external_libraries"]) 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, address_hash: address_hash,
@ -110,7 +110,8 @@ defmodule Explorer.SmartContract.Publisher do
secondary_sources: params["secondary_sources"], secondary_sources: params["secondary_sources"],
abi: abi, abi: abi,
verified_via_sourcify: params["verified_via_sourcify"], verified_via_sourcify: params["verified_via_sourcify"],
partially_verified: params["partially_verified"] partially_verified: params["partially_verified"],
is_vyper_contract: false
} }
end end

@ -1,4 +1,4 @@
defmodule Explorer.SmartContract.PublisherWorker do defmodule Explorer.SmartContract.Solidity.PublisherWorker do
@moduledoc """ @moduledoc """
Background smart contract verification worker. Background smart contract verification worker.
""" """
@ -6,7 +6,7 @@ defmodule Explorer.SmartContract.PublisherWorker do
use Que.Worker, concurrency: 5 use Que.Worker, concurrency: 5
alias Explorer.Chain.Events.Publisher, as: EventsPublisher 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 def perform({address_hash, params, external_libraries, conn}) do
result = result =

@ -1,5 +1,5 @@
# credo:disable-for-this-file # credo:disable-for-this-file
defmodule Explorer.SmartContract.Verifier do defmodule Explorer.SmartContract.Solidity.Verifier do
@moduledoc """ @moduledoc """
Module responsible to verify the Smart Contract. Module responsible to verify the Smart Contract.
@ -107,12 +107,12 @@ defmodule Explorer.SmartContract.Verifier do
blockchain_created_tx_input = blockchain_created_tx_input =
case Chain.smart_contract_creation_tx_bytecode(address_hash) do case Chain.smart_contract_creation_tx_bytecode(address_hash) do
nil -> %{init: init, created_contract_code: _created_contract_code} ->
bytecode "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 bytecode
blockchain_created_tx_input
end end
%{ %{
@ -177,6 +177,10 @@ defmodule Explorer.SmartContract.Verifier do
For more information on the swarm hash, check out: 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 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 def extract_bytecode_and_metadata_hash("0x" <> code) do
%{"metadata_hash" => metadata_hash, "bytecode" => bytecode, "compiler_version" => compiler_version} = %{"metadata_hash" => metadata_hash, "bytecode" => bytecode, "compiler_version" => compiler_version} =
extract_bytecode_and_metadata_hash(code) extract_bytecode_and_metadata_hash(code)

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -1,12 +1,12 @@
defmodule Explorer.SmartContract.Solidity.CompilerVersionTest do defmodule Explorer.SmartContract.CompilerVersionTest do
use ExUnit.Case use ExUnit.Case
doctest Explorer.SmartContract.Solidity.CompilerVersion doctest Explorer.SmartContract.CompilerVersion
alias Explorer.SmartContract.Solidity.CompilerVersion alias Explorer.SmartContract.CompilerVersion
alias Plug.Conn alias Plug.Conn
describe "fetch_versions" do describe "fetch_versions/1" do
setup do setup do
bypass = Bypass.open() bypass = Bypass.open()
@ -23,7 +23,7 @@ defmodule Explorer.SmartContract.Solidity.CompilerVersionTest do
Conn.resp(conn, 200, solc_bin_versions()) Conn.resp(conn, 200, solc_bin_versions())
end) 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 assert Enum.any?(versions, fn item -> item == "v0.4.9+commit.364da425" end) == true
end end
@ -35,7 +35,7 @@ defmodule Explorer.SmartContract.Solidity.CompilerVersionTest do
Conn.resp(conn, 200, solc_bin_versions()) Conn.resp(conn, 200, solc_bin_versions())
end) end)
assert {:ok, versions} = CompilerVersion.fetch_versions() assert {:ok, versions} = CompilerVersion.fetch_versions(:solc)
assert List.first(versions) == "latest" assert List.first(versions) == "latest"
end end
@ -44,13 +44,13 @@ defmodule Explorer.SmartContract.Solidity.CompilerVersionTest do
Conn.resp(conn, 400, ~S({"error": "bad request"})) Conn.resp(conn, 400, ~S({"error": "bad request"}))
end) end)
assert {:error, "bad request"} = CompilerVersion.fetch_versions() assert {:error, "bad request"} = CompilerVersion.fetch_versions(:solc)
end end
test "returns error when there is server error", %{bypass: bypass} do test "returns error when there is server error", %{bypass: bypass} do
Bypass.down(bypass) Bypass.down(bypass)
assert {:error, :econnrefused} = CompilerVersion.fetch_versions() assert {:error, :econnrefused} = CompilerVersion.fetch_versions(:solc)
end end
end end

@ -1,15 +1,15 @@
defmodule Explorer.SmartContract.PublisherTest do defmodule Explorer.SmartContract.Solidity.PublisherTest do
use ExUnit.Case, async: true use ExUnit.Case, async: true
use Explorer.DataCase use Explorer.DataCase
doctest Explorer.SmartContract.Publisher doctest Explorer.SmartContract.Solidity.Publisher
@moduletag timeout: :infinity @moduletag timeout: :infinity
alias Explorer.Chain.{ContractMethod, SmartContract} alias Explorer.Chain.{ContractMethod, SmartContract}
alias Explorer.{Factory, Repo} alias Explorer.{Factory, Repo}
alias Explorer.SmartContract.Publisher alias Explorer.SmartContract.Solidity.Publisher
describe "publish/2" do describe "publish/2" do
test "with valid data creates a smart_contract" 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) 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 -> Enum.each(contract_code_info.abi, fn selector ->
[parsed] = ABI.parse_specification([selector]) [parsed] = ABI.parse_specification([selector])
@ -174,7 +174,7 @@ defmodule Explorer.SmartContract.PublisherTest do
end) end)
response = Publisher.publish(contract_address.hash, params, external_libraries_form_params) 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 end
end end

@ -1,12 +1,12 @@
defmodule Explorer.SmartContract.VerifierTest do defmodule Explorer.SmartContract.Solidity.VerifierTest do
use ExUnit.Case, async: true use ExUnit.Case, async: true
use Explorer.DataCase use Explorer.DataCase
@moduletag timeout: :infinity @moduletag timeout: :infinity
doctest Explorer.SmartContract.Verifier doctest Explorer.SmartContract.Solidity.Verifier
alias Explorer.SmartContract.Verifier alias Explorer.SmartContract.Solidity.Verifier
alias Explorer.Factory alias Explorer.Factory
@code_0_4 """ @code_0_4 """

@ -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 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 # Get Rust
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y

Loading…
Cancel
Save