Vyper contract verification

pull/4745/head
Viktor Baranov 3 years ago
parent 0692487cb5
commit db4bf40cca
  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
apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex:400: Function timestamp_to_datetime/1 has no local return
lib/explorer/repo/prometheus_logger.ex:8
lib/explorer/smart_contract/publisher_worker.ex:1
lib/explorer/smart_contract/publisher_worker.ex:6
lib/explorer/smart_contract/solidity/publisher_worker.ex:1
lib/explorer/smart_contract/vyper/publisher_worker.ex:1
lib/explorer/smart_contract/solidity/publisher_worker.ex:6
lib/explorer/smart_contract/vyper/publisher_worker.ex:6
apps/explorer/lib/explorer/repo/prometheus_logger.ex:8: Function microseconds_time/1 has no local return
apps/explorer/lib/explorer/repo/prometheus_logger.ex:8: The call 'Elixir.System':convert_time_unit(__@1::any(),'native','microseconds') breaks the contract (integer(),time_unit() | 'native',time_unit() | 'native') -> integer()
lib/block_scout_web/router.ex:1
@ -21,7 +23,7 @@ lib/explorer/smart_contract/reader.ex:440
lib/indexer/fetcher/token_total_supply_on_demand.ex:16
lib/explorer/exchange_rates/source.ex:110
lib/explorer/exchange_rates/source.ex:113
lib/explorer/smart_contract/verifier.ex:89
lib/explorer/smart_contract/solidity/verifier.ex:89
lib/block_scout_web/templates/address_contract/index.html.eex:156
lib/block_scout_web/templates/address_contract/index.html.eex:199
lib/explorer/staking/stake_snapshotting.ex:15: Function do_snapshotting/7 has no local return

@ -38,7 +38,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_2-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_3-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-
@ -98,7 +98,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_2-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_3-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -122,7 +122,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_2-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_3-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -145,7 +145,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_2-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_3-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -154,7 +154,7 @@ jobs:
id: dialyzer-cache
with:
path: priv/plts
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-dialyzer-mixlockhash_2-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-dialyzer-mixlockhash_3-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-dialyzer-"
@ -185,7 +185,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_2-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_3-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -211,7 +211,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_2-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_3-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -239,7 +239,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_2-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_3-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -288,7 +288,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_2-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_3-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -350,7 +350,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_2-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_3-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -406,7 +406,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_2-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_3-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -473,7 +473,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_2-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_3-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -534,7 +534,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_2-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_3-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"

@ -1,6 +1,7 @@
## Current
### Features
- [#4745](https://github.com/blockscout/blockscout/pull/4745) - Vyper contracts verification
- [#4667](https://github.com/blockscout/blockscout/pull/4667) - Transaction Page: Add expand/collapse button for long contract method data
- [#4641](https://github.com/blockscout/blockscout/pull/4641), [#4733](https://github.com/blockscout/blockscout/pull/4733) - Improve Read Contract page logic
- [#4660](https://github.com/blockscout/blockscout/pull/4660) - Save Sourcify path instead of filename

@ -233,6 +233,7 @@ if ($contractVerificationPage.length) {
if ($(this).prop('checked')) {
$('#verify_via_flattened_code_button').show()
$('#verify_via_sourcify_button').hide()
$('#verify_vyper_contract_button').hide()
}
})
@ -240,6 +241,15 @@ if ($contractVerificationPage.length) {
if ($(this).prop('checked')) {
$('#verify_via_flattened_code_button').hide()
$('#verify_via_sourcify_button').show()
$('#verify_vyper_contract_button').hide()
}
})
$('.verify-vyper-contract').on('click', function () {
if ($(this).prop('checked')) {
$('#verify_via_flattened_code_button').hide()
$('#verify_via_sourcify_button').hide()
$('#verify_vyper_contract_button').show()
}
})
}

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

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

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

@ -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.Events.Publisher, as: EventsPublisher
alias Explorer.Chain.{Hash, SmartContract}
alias Explorer.SmartContract.Publisher
alias Explorer.SmartContract.Solidity.Publisher
alias Explorer.SmartContract.Vyper.Publisher, as: VyperPublisher
alias Explorer.ThirdPartyIntegrations.Sourcify
def verify(conn, %{"addressHash" => address_hash} = params) do
@ -196,6 +197,40 @@ defmodule BlockScoutWeb.API.RPC.ContractController do
end
end
def verify_vyper_contract(conn, %{"addressHash" => address_hash} = params) do
with {:params, {:ok, fetched_params}} <- {:params, fetch_vyper_verify_params(params)},
{:format, {:ok, casted_address_hash}} <- to_address_hash(address_hash),
{:publish, {:ok, _}} <-
{:publish, VyperPublisher.publish(address_hash, fetched_params)} do
address = Chain.address_hash_to_address_with_source_code(casted_address_hash)
render(conn, :verify, %{contract: address})
else
{:publish,
{:error,
%Ecto.Changeset{
errors: [
address_hash:
{"has already been taken",
[
constraint: :unique,
constraint_name: "smart_contracts_address_hash_index"
]}
]
}}} ->
render(conn, :error, error: "Smart-contract already verified.")
{:publish, _} ->
render(conn, :error, error: "Something went wrong while publishing the contract.")
{:format, :error} ->
render(conn, :error, error: "Invalid address hash")
{:params, {:error, error}} ->
render(conn, :error, error: error)
end
end
def publish_without_broadcast(
%{"addressHash" => address_hash, "abi" => abi, "compilationTargetFilePath" => file_path} = input
) do
@ -416,6 +451,15 @@ defmodule BlockScoutWeb.API.RPC.ContractController do
|> parse_optimization_runs()
end
defp fetch_vyper_verify_params(params) do
{:ok, %{}}
|> required_param(params, "addressHash", "address_hash")
|> required_param(params, "name", "name")
|> required_param(params, "compilerVersion", "compiler_version")
|> required_param(params, "contractSourceCode", "contract_source_code")
|> optional_param(params, "constructorArguments", "constructor_arguments")
end
defp parse_optimization_runs({:ok, %{"optimization_runs" => runs} = opts}) when is_bitstring(runs) do
{:ok, Map.put(opts, "optimization_runs", 200)}
end

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

@ -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 %{
name: "getabi",
description: "Get ABI for verified contract. Also available through a GraphQL 'addresses' query.",
@ -2750,7 +2820,8 @@ defmodule BlockScoutWeb.Etherscan do
@contract_getabi_action,
@contract_getsourcecode_action,
@contract_verify_action,
@contract_verify_via_sourcify_action
@contract_verify_via_sourcify_action,
@contract_verify_vyper_contract_action
]
}

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

@ -51,7 +51,7 @@
<div class="d-none d-sm-block d-md-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>
<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 class="row">
<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-inner-wrapper">
<%= label f, "Verification via" %>
<%= label f, "Verify" %>
<div class="center-column">
<div class="form-radios-group vertical">
<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" %>
<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>
<%= 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">
<%= 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>
<%= 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>
<%= 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/>
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/>
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>
@ -86,6 +95,14 @@
style: "display: none;",
"data-button-loading": "animation"
) %>
<%= link(
gettext("Next"),
to: address_verify_vyper_contract_path(@conn, :new, @address_hash),
id: "verify_vyper_contract_button",
class: "btn-full-primary mr-2",
style: "display: none;",
"data-button-loading": "animation"
) %>
<%=
link(
gettext("Cancel"),

@ -15,7 +15,7 @@
</div>
<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,
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." %>
<%= case @log.transaction do %>
<% %{to_address: %{hash: hash}} -> %>
<% path =
if Application.get_env(:explorer, Explorer.ThirdPartyIntegrations.Sourcify)[:enabled] do
address_verify_contract_path(@conn, :new, hash)
else
address_verify_contract_via_flattened_code_path(@conn, :new, hash)
end
%>
<% path = address_verify_contract_path(@conn, :new, hash) %>
<%= gettext "Verify the contract " %><a href="<%= path %>"><%= gettext "here" %></a>
<% _ -> %>
<%= nil %>

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

@ -5,13 +5,7 @@
<%= if minimal_proxy_template do %>
<%= render BlockScoutWeb.CommonComponentsView, "_minimal_proxy_pattern.html", address_hash: metadata_for_verification.address_hash, conn: @conn %>
<% else %>
<% path =
if Application.get_env(:explorer, Explorer.ThirdPartyIntegrations.Sourcify)[:enabled] do
address_verify_contract_path(@conn, :new, @address.hash)
else
address_verify_contract_via_flattened_code_path(@conn, :new, @address.hash)
end
%>
<% path = address_verify_contract_path(@conn, :new, @address.hash) %>
<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(
metadata_for_verification.address_hash,

@ -8,13 +8,7 @@
<%= gettext "To see accurate decoded input data, the contract must be verified." %>
<%= case @transaction do %>
<% %{to_address: %{hash: hash}} -> %>
<% path =
if Application.get_env(:explorer, Explorer.ThirdPartyIntegrations.Sourcify)[:enabled] do
address_verify_contract_path(@conn, :new, hash)
else
address_verify_contract_via_flattened_code_path(@conn, :new, hash)
end
%>
<% path = address_verify_contract_path(@conn, :new, hash) %>
<%= gettext "Verify the contract " %><a href="<%= path %>"><%= gettext "here" %></a>
<% _ -> %>
<%= nil %>

@ -6,13 +6,7 @@
<%= gettext "To see accurate decoded input data, the contract must be verified." %>
<%= case @transaction do %>
<% %{to_address: %{hash: hash}} -> %>
<% path =
if Application.get_env(:explorer, Explorer.ThirdPartyIntegrations.Sourcify)[:enabled] do
address_verify_contract_path(@conn, :new, hash)
else
address_verify_contract_via_flattened_code_path(@conn, :new, hash)
end
%>
<% path = address_verify_contract_path(@conn, :new, hash) %>
<%= gettext "Verify the contract " %><a href="<%= path %>"><%= gettext "here" %></a>
<% _ -> %>
<%= 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
)
# if Application.get_env(:explorer, Explorer.ThirdPartyIntegrations.Sourcify)[:enabled] do
# resources(
# "/contract_verifications",
# AddressContractVerificationController,
# only: [:new],
# as: :verify_contract
# )
# else
# resources(
# "/contract_verifications",
# AddressContractVerificationViaFlattenedCodeController,
# only: [:new],
# as: :verify_contract
# )
# end
resources(
"/verify-via-flattened-code",
AddressContractVerificationViaFlattenedCodeController,
@ -162,6 +146,13 @@ defmodule BlockScoutWeb.WebRouter do
as: :verify_contract_via_json
)
resources(
"/verify-vyper-contract",
AddressContractVerificationVyperController,
only: [:new],
as: :verify_vyper_contract
)
resources(
"/read-contract",
AddressReadContractController,

@ -125,6 +125,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:156
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:66
msgid "ABI-encoded Constructor Arguments (if required by the contract)"
msgstr ""
@ -170,7 +171,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:16
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:26 lib/block_scout_web/views/address_view.ex:104
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:20 lib/block_scout_web/views/address_view.ex:104
msgid "Address"
msgstr ""
@ -199,7 +200,7 @@ msgid "All"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:18
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:12
msgid "All functions displayed below are from ABI of that contract. In order to verify current contract, proceed with"
msgstr ""
@ -434,9 +435,10 @@ msgid "Call Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:91
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:108
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:305
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:56 lib/block_scout_web/templates/api_docs/_action_tile.html.eex:47
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:56
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:93 lib/block_scout_web/templates/api_docs/_action_tile.html.eex:47
#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:54
msgid "Cancel"
msgstr ""
@ -526,6 +528,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:71
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:44
msgid "Compiler"
msgstr ""
@ -552,7 +555,8 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:4
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:13
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:4 lib/block_scout_web/templates/tokens/holder/index.html.eex:17
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:4
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:8 lib/block_scout_web/templates/tokens/holder/index.html.eex:17
msgid "Connection Lost"
msgstr ""
@ -596,7 +600,8 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:18
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:27
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:13 lib/block_scout_web/views/address_view.ex:102
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:13
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:22 lib/block_scout_web/views/address_view.ex:102
msgid "Contract Address"
msgstr ""
@ -629,12 +634,13 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:38
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:33
msgid "Contract Name"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:22
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:16
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:10
msgid "Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB"
msgstr ""
@ -817,9 +823,9 @@ msgid "DApp for Staking %{symbol} tokens"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:107
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:101
#: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:7 lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:21
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:110
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:104
msgid "Data"
msgstr ""
@ -840,10 +846,10 @@ msgid "Decimals"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:38
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:44 lib/block_scout_web/templates/address_logs/_logs.html.eex:59
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:39 lib/block_scout_web/templates/transaction_log/_logs.html.eex:47
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:62
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:32
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:38 lib/block_scout_web/templates/address_logs/_logs.html.eex:53
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:33 lib/block_scout_web/templates/transaction_log/_logs.html.eex:41
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:56
msgid "Decoded"
msgstr ""
@ -943,8 +949,8 @@ msgid "ERC-721 "
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:95
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:95 lib/block_scout_web/templates/smart_contract/_functions.html.eex:131
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:89
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:89 lib/block_scout_web/templates/smart_contract/_functions.html.eex:125
msgid "ETH"
msgstr ""
@ -1047,13 +1053,13 @@ msgid "External libraries"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:39
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:33
msgid "Failed to decode input data."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:41
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:42
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:35
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:36
msgid "Failed to decode log data."
msgstr ""
@ -1067,11 +1073,6 @@ msgstr ""
msgid "Fetching tokens..."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:35
msgid "Flattened source code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/admin/dashboard/index.html.eex:16
msgid "For any existing contracts in the database, insert all ABI entries into the contract_methods table. Use this in case you have verified smart contracts before early March 2019 and you want other contracts with the same functions to show those ABI's as candidate matches."
@ -1165,7 +1166,7 @@ msgid "However, in general, the"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:25
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:19
msgid "IMPORTANT: This information is a best guess based on similar functions from other verified contracts."
msgstr ""
@ -1340,9 +1341,10 @@ msgid "Loading..."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:72
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:81
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:299
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:50
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:87
msgid "Loading...."
msgstr ""
@ -1352,7 +1354,7 @@ msgid "Log Data"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:120
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:114
msgid "Log Index"
msgstr ""
@ -1512,14 +1514,13 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:9
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:18
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:9
msgid "New Smart Contract Verification"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:75
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:82
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:84
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:91 lib/block_scout_web/templates/address_contract_verification/new.html.eex:99
msgid "Next"
msgstr ""
@ -1672,7 +1673,7 @@ msgid "Potential Reward Share"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:24
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:18
msgid "Potential matches from our contract method database:"
msgstr ""
@ -1710,7 +1711,7 @@ msgid "QR Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:99
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:93
msgid "Query"
msgstr ""
@ -1776,6 +1777,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:302
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:53
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:90
msgid "Reset"
msgstr ""
@ -1944,11 +1946,6 @@ msgstr ""
msgid "Sources and Metadata JSON"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:40
msgid "Sourcify: Sources and metadata JSON file"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/stakes/_stakes_modal_stake.html.eex:7
msgid "Stake"
@ -2089,7 +2086,7 @@ msgid "The block height of a particular block is defined as the number of blocks
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:43
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:37
msgid "The fallback function is executed on a call to the contract if none of the other functions match the given function signature, or if no data was supplied at all and there is no receive Ether function. The fallback function always receives data, but in order to also receive Ether it must be marked payable."
msgstr ""
@ -2129,7 +2126,7 @@ msgid "The percentage of stake in a single pool relative to the total amount sta
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:45
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:39
msgid "The receive function is executed on a call to the contract with empty calldata. This is the function that is executed on plain Ether transfers (e.g. via .send() or .transfer()). If no such function exists, but a payable fallback function exists, the fallback function will be called on a plain Ether transfer. If neither a receive Ether nor a payable fallback function is present, the contract cannot receive Ether through regular transactions and throws an exception."
msgstr ""
@ -2330,7 +2327,7 @@ msgid "To"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:26
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:20
msgid "To have guaranteed accuracy, use the link above to verify the contract's source code."
msgstr ""
@ -2436,8 +2433,8 @@ msgid "Topic"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:77
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:80
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:71
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:74
msgid "Topics"
msgstr ""
@ -2482,7 +2479,7 @@ msgid "Trade STAKE on BitMax"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:25
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:19
#: lib/block_scout_web/views/transaction_view.ex:463
msgid "Transaction"
msgstr ""
@ -2711,19 +2708,20 @@ msgstr ""
#: lib/block_scout_web/templates/address_contract/index.html.eex:24
#: lib/block_scout_web/templates/address_contract/index.html.eex:158 lib/block_scout_web/templates/address_contract/index.html.eex:170
#: lib/block_scout_web/templates/address_contract/index.html.eex:201 lib/block_scout_web/templates/address_contract/index.html.eex:213
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:19
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:13
msgid "Verify & Publish"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:301
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:52
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:89
msgid "Verify & publish"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:16
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:18 lib/block_scout_web/templates/transaction_log/_logs.html.eex:16
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:10
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:12 lib/block_scout_web/templates/transaction_log/_logs.html.eex:10
msgid "Verify the contract "
msgstr ""
@ -2774,7 +2772,7 @@ msgid "View transaction %{transaction} on %{subnetwork}"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:130
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:124
msgid "WEI"
msgstr ""
@ -2824,7 +2822,7 @@ msgid "Working Stake Amount"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:99
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:93
msgid "Write"
msgstr ""
@ -2939,7 +2937,7 @@ msgid "custom RPC"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:43
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:37
msgid "fallback"
msgstr ""
@ -2949,8 +2947,8 @@ msgid "false"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:16
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:18 lib/block_scout_web/templates/transaction_log/_logs.html.eex:16
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:10
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:12 lib/block_scout_web/templates/transaction_log/_logs.html.eex:10
msgid "here"
msgstr ""
@ -2970,7 +2968,7 @@ msgid "of"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:21
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:15
msgid "page"
msgstr ""
@ -2980,7 +2978,7 @@ msgid "pool owner"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:45
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:39
msgid "receive"
msgstr ""
@ -3046,3 +3044,33 @@ msgstr ""
#: lib/block_scout_web/templates/block/overview.html.eex:209
msgid "burned from transactions included in the block (Base fee (per unit of gas) * Gas Used)."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:55
msgid "Enter the Vyper Contract Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:18
msgid "New Solidity Smart Contract Verification"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:13
msgid "New Vyper Smart Contract Verification"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:41
msgid "Via Sourcify: Sources and metadata JSON file"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:35
msgid "Via flattened source code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:47
msgid "Vyper contract"
msgstr ""

@ -125,6 +125,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:156
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:66
msgid "ABI-encoded Constructor Arguments (if required by the contract)"
msgstr ""
@ -170,7 +171,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:16
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:26 lib/block_scout_web/views/address_view.ex:104
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:20 lib/block_scout_web/views/address_view.ex:104
msgid "Address"
msgstr ""
@ -199,7 +200,7 @@ msgid "All"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:18
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:12
msgid "All functions displayed below are from ABI of that contract. In order to verify current contract, proceed with"
msgstr ""
@ -434,9 +435,10 @@ msgid "Call Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:91
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:108
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:305
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:56 lib/block_scout_web/templates/api_docs/_action_tile.html.eex:47
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:56
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:93 lib/block_scout_web/templates/api_docs/_action_tile.html.eex:47
#: lib/block_scout_web/templates/api_docs/_eth_rpc_item.html.eex:54
msgid "Cancel"
msgstr ""
@ -526,6 +528,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:71
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:44
msgid "Compiler"
msgstr ""
@ -552,7 +555,8 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:4
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:13
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:4 lib/block_scout_web/templates/tokens/holder/index.html.eex:17
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:4
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:8 lib/block_scout_web/templates/tokens/holder/index.html.eex:17
msgid "Connection Lost"
msgstr ""
@ -596,7 +600,8 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:18
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:27
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:13 lib/block_scout_web/views/address_view.ex:102
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:13
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:22 lib/block_scout_web/views/address_view.ex:102
msgid "Contract Address"
msgstr ""
@ -629,12 +634,13 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:38
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:33
msgid "Contract Name"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract/index.html.eex:22
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:16
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:10
msgid "Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB"
msgstr ""
@ -817,9 +823,9 @@ msgid "DApp for Staking %{symbol} tokens"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:107
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:101
#: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:7 lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:21
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:110
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:104
msgid "Data"
msgstr ""
@ -840,10 +846,10 @@ msgid "Decimals"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:38
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:44 lib/block_scout_web/templates/address_logs/_logs.html.eex:59
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:39 lib/block_scout_web/templates/transaction_log/_logs.html.eex:47
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:62
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:32
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:38 lib/block_scout_web/templates/address_logs/_logs.html.eex:53
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:33 lib/block_scout_web/templates/transaction_log/_logs.html.eex:41
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:56
msgid "Decoded"
msgstr ""
@ -943,8 +949,8 @@ msgid "ERC-721 "
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:95
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:95 lib/block_scout_web/templates/smart_contract/_functions.html.eex:131
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:89
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:89 lib/block_scout_web/templates/smart_contract/_functions.html.eex:125
msgid "ETH"
msgstr ""
@ -1047,13 +1053,13 @@ msgid "External libraries"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:39
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:33
msgid "Failed to decode input data."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:41
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:42
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:35
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:36
msgid "Failed to decode log data."
msgstr ""
@ -1067,11 +1073,6 @@ msgstr ""
msgid "Fetching tokens..."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:35
msgid "Flattened source code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/admin/dashboard/index.html.eex:16
msgid "For any existing contracts in the database, insert all ABI entries into the contract_methods table. Use this in case you have verified smart contracts before early March 2019 and you want other contracts with the same functions to show those ABI's as candidate matches."
@ -1165,7 +1166,7 @@ msgid "However, in general, the"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:25
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:19
msgid "IMPORTANT: This information is a best guess based on similar functions from other verified contracts."
msgstr ""
@ -1340,9 +1341,10 @@ msgid "Loading..."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:72
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:81
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:299
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:50
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:87
msgid "Loading...."
msgstr ""
@ -1352,7 +1354,7 @@ msgid "Log Data"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:120
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:114
msgid "Log Index"
msgstr ""
@ -1512,14 +1514,13 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:9
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:18
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:9
msgid "New Smart Contract Verification"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:75
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:82
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:84
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:91 lib/block_scout_web/templates/address_contract_verification/new.html.eex:99
msgid "Next"
msgstr ""
@ -1672,7 +1673,7 @@ msgid "Potential Reward Share"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:24
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:18
msgid "Potential matches from our contract method database:"
msgstr ""
@ -1710,7 +1711,7 @@ msgid "QR Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:99
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:93
msgid "Query"
msgstr ""
@ -1776,6 +1777,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:302
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:53
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:90
msgid "Reset"
msgstr ""
@ -1944,11 +1946,6 @@ msgstr ""
msgid "Sources and Metadata JSON"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:40
msgid "Sourcify: Sources and metadata JSON file"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/stakes/_stakes_modal_stake.html.eex:7
msgid "Stake"
@ -2089,7 +2086,7 @@ msgid "The block height of a particular block is defined as the number of blocks
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:43
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:37
msgid "The fallback function is executed on a call to the contract if none of the other functions match the given function signature, or if no data was supplied at all and there is no receive Ether function. The fallback function always receives data, but in order to also receive Ether it must be marked payable."
msgstr ""
@ -2129,7 +2126,7 @@ msgid "The percentage of stake in a single pool relative to the total amount sta
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:45
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:39
msgid "The receive function is executed on a call to the contract with empty calldata. This is the function that is executed on plain Ether transfers (e.g. via .send() or .transfer()). If no such function exists, but a payable fallback function exists, the fallback function will be called on a plain Ether transfer. If neither a receive Ether nor a payable fallback function is present, the contract cannot receive Ether through regular transactions and throws an exception."
msgstr ""
@ -2330,7 +2327,7 @@ msgid "To"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:26
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:20
msgid "To have guaranteed accuracy, use the link above to verify the contract's source code."
msgstr ""
@ -2436,8 +2433,8 @@ msgid "Topic"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:77
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:80
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:71
#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:74
msgid "Topics"
msgstr ""
@ -2482,7 +2479,7 @@ msgid "Trade STAKE on BitMax"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:25
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:19
#: lib/block_scout_web/views/transaction_view.ex:463
msgid "Transaction"
msgstr ""
@ -2711,19 +2708,20 @@ msgstr ""
#: lib/block_scout_web/templates/address_contract/index.html.eex:24
#: lib/block_scout_web/templates/address_contract/index.html.eex:158 lib/block_scout_web/templates/address_contract/index.html.eex:170
#: lib/block_scout_web/templates/address_contract/index.html.eex:201 lib/block_scout_web/templates/address_contract/index.html.eex:213
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:19
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:13
msgid "Verify & Publish"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:301
#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:52
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:89
msgid "Verify & publish"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:16
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:18 lib/block_scout_web/templates/transaction_log/_logs.html.eex:16
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:10
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:12 lib/block_scout_web/templates/transaction_log/_logs.html.eex:10
msgid "Verify the contract "
msgstr ""
@ -2774,7 +2772,7 @@ msgid "View transaction %{transaction} on %{subnetwork}"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:130
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:124
msgid "WEI"
msgstr ""
@ -2824,7 +2822,7 @@ msgid "Working Stake Amount"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:99
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:93
msgid "Write"
msgstr ""
@ -2939,7 +2937,7 @@ msgid "custom RPC"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:43
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:37
msgid "fallback"
msgstr ""
@ -2949,8 +2947,8 @@ msgid "false"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:16
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:18 lib/block_scout_web/templates/transaction_log/_logs.html.eex:16
#: lib/block_scout_web/templates/address_logs/_logs.html.eex:10
#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:12 lib/block_scout_web/templates/transaction_log/_logs.html.eex:10
msgid "here"
msgstr ""
@ -2970,7 +2968,7 @@ msgid "of"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:21
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:15
msgid "page"
msgstr ""
@ -2980,7 +2978,7 @@ msgid "pool owner"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:45
#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:39
msgid "receive"
msgstr ""
@ -3046,3 +3044,33 @@ msgstr ""
#: lib/block_scout_web/templates/block/overview.html.eex:209
msgid "burned from transactions included in the block (Base fee (per unit of gas) * Gas Used)."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:55
msgid "Enter the Vyper Contract Code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:18
msgid "New Solidity Smart Contract Verification"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification_vyper/new.html.eex:13
msgid "New Vyper Smart Contract Verification"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:41
msgid "Via Sourcify: Sources and metadata JSON file"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:35
msgid "Via flattened source code"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:47
msgid "Vyper contract"
msgstr ""

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

@ -6,6 +6,7 @@
format: "compact",
ignore: ["Config.HTTPS"],
ignore_files: [
"lib/explorer/smart_contract/solidity/code_compiler.ex"
"lib/explorer/smart_contract/solidity/code_compiler.ex",
"lib/explorer/smart_contract/vyper/code_compiler.ex"
]
]

@ -47,6 +47,7 @@ defmodule Explorer.Application do
Supervisor.child_spec({Task.Supervisor, name: Explorer.GenesisDataTaskSupervisor}, id: GenesisDataTaskSupervisor),
Supervisor.child_spec({Task.Supervisor, name: Explorer.TaskSupervisor}, id: Explorer.TaskSupervisor),
Explorer.SmartContract.SolcDownloader,
Explorer.SmartContract.VyperDownloader,
{Registry, keys: :duplicate, name: Registry.ChainEvents, id: Registry.ChainEvents},
{Admin.Recovery, [[], [name: Admin.Recovery]]},
TransactionCount,

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

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

@ -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
alias Explorer.SmartContract.Solidity.CompilerVersion
alias Explorer.SmartContract.CompilerVersion
@latest_compiler_refetch_time :timer.minutes(30)
@ -16,7 +16,7 @@ defmodule Explorer.SmartContract.SolcDownloader do
path
else
compiler_versions =
case CompilerVersion.fetch_versions() do
case CompilerVersion.fetch_versions(:solc) do
{:ok, compiler_versions} ->
compiler_versions

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

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

@ -1,5 +1,5 @@
# credo:disable-for-this-file
defmodule Explorer.SmartContract.Verifier do
defmodule Explorer.SmartContract.Solidity.Verifier do
@moduledoc """
Module responsible to verify the Smart Contract.
@ -107,12 +107,12 @@ defmodule Explorer.SmartContract.Verifier do
blockchain_created_tx_input =
case Chain.smart_contract_creation_tx_bytecode(address_hash) do
nil ->
bytecode
%{init: init, created_contract_code: _created_contract_code} ->
"0x" <> init_without_0x = init
init_without_0x
blockchain_created_tx_input_with_0x ->
"0x" <> blockchain_created_tx_input = blockchain_created_tx_input_with_0x
blockchain_created_tx_input
_ ->
bytecode
end
%{
@ -177,6 +177,10 @@ defmodule Explorer.SmartContract.Verifier do
For more information on the swarm hash, check out:
https://solidity.readthedocs.io/en/v0.5.3/metadata.html#encoding-of-the-metadata-hash-in-the-bytecode
"""
def extract_bytecode_and_metadata_hash(nil) do
%{"metadata_hash" => nil, "bytecode" => nil, "compiler_version" => nil}
end
def extract_bytecode_and_metadata_hash("0x" <> code) do
%{"metadata_hash" => metadata_hash, "bytecode" => bytecode, "compiler_version" => compiler_version} =
extract_bytecode_and_metadata_hash(code)

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

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

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

@ -2,6 +2,17 @@ FROM bitwalker/alpine-elixir-phoenix:1.12
RUN apk --no-cache --update add alpine-sdk gmp-dev automake libtool inotify-tools autoconf python3 file
ENV GLIBC_REPO=https://github.com/sgerrand/alpine-pkg-glibc
ENV GLIBC_VERSION=2.30-r0
RUN set -ex && \
apk --update add libstdc++ curl ca-certificates && \
for pkg in glibc-${GLIBC_VERSION} glibc-bin-${GLIBC_VERSION}; \
do curl -sSL ${GLIBC_REPO}/releases/download/${GLIBC_VERSION}/${pkg}.apk -o /tmp/${pkg}.apk; done && \
apk add --allow-untrusted /tmp/*.apk && \
rm -v /tmp/*.apk && \
/usr/glibc-compat/sbin/ldconfig /lib /usr/glibc-compat/lib
# Get Rust
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y

Loading…
Cancel
Save