parent
b40758e36f
commit
51e7fd0571
@ -0,0 +1,541 @@ |
||||
/* |
||||
* The MIT License |
||||
* Copyright (c) 2012 Matias Meno <m@tias.me> |
||||
*/ |
||||
|
||||
@-webkit-keyframes passing-through { |
||||
0% { |
||||
opacity: 0; |
||||
-webkit-transform: translateY(40px); |
||||
-moz-transform: translateY(40px); |
||||
-ms-transform: translateY(40px); |
||||
-o-transform: translateY(40px); |
||||
transform: translateY(40px); |
||||
} |
||||
30%, |
||||
70% { |
||||
opacity: 1; |
||||
-webkit-transform: translateY(0px); |
||||
-moz-transform: translateY(0px); |
||||
-ms-transform: translateY(0px); |
||||
-o-transform: translateY(0px); |
||||
transform: translateY(0px); |
||||
} |
||||
100% { |
||||
opacity: 0; |
||||
-webkit-transform: translateY(-40px); |
||||
-moz-transform: translateY(-40px); |
||||
-ms-transform: translateY(-40px); |
||||
-o-transform: translateY(-40px); |
||||
transform: translateY(-40px); |
||||
} |
||||
} |
||||
|
||||
@-moz-keyframes passing-through { |
||||
0% { |
||||
opacity: 0; |
||||
-webkit-transform: translateY(40px); |
||||
-moz-transform: translateY(40px); |
||||
-ms-transform: translateY(40px); |
||||
-o-transform: translateY(40px); |
||||
transform: translateY(40px); |
||||
} |
||||
30%, |
||||
70% { |
||||
opacity: 1; |
||||
-webkit-transform: translateY(0px); |
||||
-moz-transform: translateY(0px); |
||||
-ms-transform: translateY(0px); |
||||
-o-transform: translateY(0px); |
||||
transform: translateY(0px); |
||||
} |
||||
100% { |
||||
opacity: 0; |
||||
-webkit-transform: translateY(-40px); |
||||
-moz-transform: translateY(-40px); |
||||
-ms-transform: translateY(-40px); |
||||
-o-transform: translateY(-40px); |
||||
transform: translateY(-40px); |
||||
} |
||||
} |
||||
|
||||
@keyframes passing-through { |
||||
0% { |
||||
opacity: 0; |
||||
-webkit-transform: translateY(40px); |
||||
-moz-transform: translateY(40px); |
||||
-ms-transform: translateY(40px); |
||||
-o-transform: translateY(40px); |
||||
transform: translateY(40px); |
||||
} |
||||
30%, |
||||
70% { |
||||
opacity: 1; |
||||
-webkit-transform: translateY(0px); |
||||
-moz-transform: translateY(0px); |
||||
-ms-transform: translateY(0px); |
||||
-o-transform: translateY(0px); |
||||
transform: translateY(0px); |
||||
} |
||||
100% { |
||||
opacity: 0; |
||||
-webkit-transform: translateY(-40px); |
||||
-moz-transform: translateY(-40px); |
||||
-ms-transform: translateY(-40px); |
||||
-o-transform: translateY(-40px); |
||||
transform: translateY(-40px); |
||||
} |
||||
} |
||||
|
||||
@-webkit-keyframes slide-in { |
||||
0% { |
||||
opacity: 0; |
||||
-webkit-transform: translateY(40px); |
||||
-moz-transform: translateY(40px); |
||||
-ms-transform: translateY(40px); |
||||
-o-transform: translateY(40px); |
||||
transform: translateY(40px); |
||||
} |
||||
30% { |
||||
opacity: 1; |
||||
-webkit-transform: translateY(0px); |
||||
-moz-transform: translateY(0px); |
||||
-ms-transform: translateY(0px); |
||||
-o-transform: translateY(0px); |
||||
transform: translateY(0px); |
||||
} |
||||
} |
||||
|
||||
@-moz-keyframes slide-in { |
||||
0% { |
||||
opacity: 0; |
||||
-webkit-transform: translateY(40px); |
||||
-moz-transform: translateY(40px); |
||||
-ms-transform: translateY(40px); |
||||
-o-transform: translateY(40px); |
||||
transform: translateY(40px); |
||||
} |
||||
30% { |
||||
opacity: 1; |
||||
-webkit-transform: translateY(0px); |
||||
-moz-transform: translateY(0px); |
||||
-ms-transform: translateY(0px); |
||||
-o-transform: translateY(0px); |
||||
transform: translateY(0px); |
||||
} |
||||
} |
||||
|
||||
@keyframes slide-in { |
||||
0% { |
||||
opacity: 0; |
||||
-webkit-transform: translateY(40px); |
||||
-moz-transform: translateY(40px); |
||||
-ms-transform: translateY(40px); |
||||
-o-transform: translateY(40px); |
||||
transform: translateY(40px); |
||||
} |
||||
30% { |
||||
opacity: 1; |
||||
-webkit-transform: translateY(0px); |
||||
-moz-transform: translateY(0px); |
||||
-ms-transform: translateY(0px); |
||||
-o-transform: translateY(0px); |
||||
transform: translateY(0px); |
||||
} |
||||
} |
||||
|
||||
@-webkit-keyframes pulse { |
||||
0% { |
||||
-webkit-transform: scale(1); |
||||
-moz-transform: scale(1); |
||||
-ms-transform: scale(1); |
||||
-o-transform: scale(1); |
||||
transform: scale(1); |
||||
} |
||||
10% { |
||||
-webkit-transform: scale(1.1); |
||||
-moz-transform: scale(1.1); |
||||
-ms-transform: scale(1.1); |
||||
-o-transform: scale(1.1); |
||||
transform: scale(1.1); |
||||
} |
||||
20% { |
||||
-webkit-transform: scale(1); |
||||
-moz-transform: scale(1); |
||||
-ms-transform: scale(1); |
||||
-o-transform: scale(1); |
||||
transform: scale(1); |
||||
} |
||||
} |
||||
|
||||
@-moz-keyframes pulse { |
||||
0% { |
||||
-webkit-transform: scale(1); |
||||
-moz-transform: scale(1); |
||||
-ms-transform: scale(1); |
||||
-o-transform: scale(1); |
||||
transform: scale(1); |
||||
} |
||||
10% { |
||||
-webkit-transform: scale(1.1); |
||||
-moz-transform: scale(1.1); |
||||
-ms-transform: scale(1.1); |
||||
-o-transform: scale(1.1); |
||||
transform: scale(1.1); |
||||
} |
||||
20% { |
||||
-webkit-transform: scale(1); |
||||
-moz-transform: scale(1); |
||||
-ms-transform: scale(1); |
||||
-o-transform: scale(1); |
||||
transform: scale(1); |
||||
} |
||||
} |
||||
|
||||
@keyframes pulse { |
||||
0% { |
||||
-webkit-transform: scale(1); |
||||
-moz-transform: scale(1); |
||||
-ms-transform: scale(1); |
||||
-o-transform: scale(1); |
||||
transform: scale(1); |
||||
} |
||||
10% { |
||||
-webkit-transform: scale(1.1); |
||||
-moz-transform: scale(1.1); |
||||
-ms-transform: scale(1.1); |
||||
-o-transform: scale(1.1); |
||||
transform: scale(1.1); |
||||
} |
||||
20% { |
||||
-webkit-transform: scale(1); |
||||
-moz-transform: scale(1); |
||||
-ms-transform: scale(1); |
||||
-o-transform: scale(1); |
||||
transform: scale(1); |
||||
} |
||||
} |
||||
|
||||
.dropzone-1, |
||||
.dropzone-1 * { |
||||
box-sizing: border-box; |
||||
} |
||||
|
||||
.dropzone-1 { |
||||
min-height: 150px; |
||||
border: 2px solid rgba(0, 0, 0, 0.3); |
||||
background: white; |
||||
padding: 20px 20px; |
||||
} |
||||
|
||||
.dropzone-1.dz-clickable { |
||||
cursor: pointer; |
||||
} |
||||
|
||||
.dropzone-1.dz-clickable * { |
||||
cursor: default; |
||||
} |
||||
|
||||
.dropzone-1.dz-clickable .dz-message, |
||||
.dropzone-1.dz-clickable .dz-message * { |
||||
cursor: pointer; |
||||
} |
||||
|
||||
.dropzone-1.dz-started .dz-message { |
||||
display: none; |
||||
} |
||||
|
||||
.dropzone-1.dz-drag-hover { |
||||
border-style: solid; |
||||
} |
||||
|
||||
.dropzone-1.dz-drag-hover .dz-message { |
||||
opacity: 0.5; |
||||
} |
||||
|
||||
.dropzone-1 .dz-message { |
||||
text-align: center; |
||||
margin: 2em 0; |
||||
} |
||||
|
||||
.dropzone-1 .dz-message .dz-button { |
||||
background: none; |
||||
color: inherit; |
||||
border: none; |
||||
padding: 0; |
||||
font: inherit; |
||||
cursor: pointer; |
||||
outline: inherit; |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview { |
||||
position: relative; |
||||
display: inline-block; |
||||
vertical-align: top; |
||||
margin: 16px; |
||||
min-height: 100px; |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview:hover { |
||||
z-index: 1000; |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview:hover .dz-details { |
||||
opacity: 1; |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview.dz-file-preview .dz-image { |
||||
border-radius: 20px; |
||||
background: #999; |
||||
background: linear-gradient(to bottom, #eee, #ddd); |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview.dz-file-preview .dz-details { |
||||
opacity: 1; |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview.dz-image-preview { |
||||
background: white; |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview.dz-image-preview .dz-details { |
||||
-webkit-transition: opacity 0.2s linear; |
||||
-moz-transition: opacity 0.2s linear; |
||||
-ms-transition: opacity 0.2s linear; |
||||
-o-transition: opacity 0.2s linear; |
||||
transition: opacity 0.2s linear; |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview .dz-remove { |
||||
font-size: 14px; |
||||
text-align: center; |
||||
display: block; |
||||
cursor: pointer; |
||||
border: none; |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview .dz-remove:hover { |
||||
text-decoration: underline; |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview:hover .dz-details { |
||||
opacity: 1; |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview .dz-details { |
||||
z-index: 20; |
||||
position: absolute; |
||||
top: 0; |
||||
left: 0; |
||||
opacity: 0; |
||||
font-size: 13px; |
||||
min-width: 100%; |
||||
max-width: 100%; |
||||
padding: 2em 1em; |
||||
text-align: center; |
||||
color: rgba(0, 0, 0, 0.9); |
||||
line-height: 150%; |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview .dz-details .dz-size { |
||||
margin-bottom: 1em; |
||||
font-size: 16px; |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview .dz-details .dz-filename { |
||||
white-space: nowrap; |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview .dz-details .dz-filename:hover span { |
||||
border: 1px solid rgba(200, 200, 200, 0.8); |
||||
background-color: rgba(255, 255, 255, 0.8); |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview .dz-details .dz-filename:not(:hover) { |
||||
overflow: hidden; |
||||
text-overflow: ellipsis; |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview .dz-details .dz-filename:not(:hover) span { |
||||
border: 1px solid transparent; |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview .dz-details .dz-filename span, |
||||
.dropzone-1 .dz-preview .dz-details .dz-size span { |
||||
background-color: rgba(255, 255, 255, 0.4); |
||||
padding: 0 0.4em; |
||||
border-radius: 3px; |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview:hover .dz-image img { |
||||
-webkit-transform: scale(1.05, 1.05); |
||||
-moz-transform: scale(1.05, 1.05); |
||||
-ms-transform: scale(1.05, 1.05); |
||||
-o-transform: scale(1.05, 1.05); |
||||
transform: scale(1.05, 1.05); |
||||
-webkit-filter: blur(8px); |
||||
filter: blur(8px); |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview .dz-image { |
||||
border-radius: 20px; |
||||
overflow: hidden; |
||||
width: 120px; |
||||
height: 120px; |
||||
position: relative; |
||||
display: block; |
||||
z-index: 10; |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview .dz-image img { |
||||
display: block; |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview.dz-success .dz-success-mark { |
||||
-webkit-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1); |
||||
-moz-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1); |
||||
-ms-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1); |
||||
-o-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1); |
||||
animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1); |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview.dz-error .dz-error-mark { |
||||
opacity: 1; |
||||
-webkit-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1); |
||||
-moz-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1); |
||||
-ms-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1); |
||||
-o-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1); |
||||
animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1); |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview .dz-success-mark, |
||||
.dropzone-1 .dz-preview .dz-error-mark { |
||||
pointer-events: none; |
||||
opacity: 0; |
||||
z-index: 500; |
||||
position: absolute; |
||||
display: block; |
||||
top: 50%; |
||||
left: 50%; |
||||
margin-left: -27px; |
||||
margin-top: -27px; |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview .dz-success-mark svg, |
||||
.dropzone-1 .dz-preview .dz-error-mark svg { |
||||
display: block; |
||||
width: 54px; |
||||
height: 54px; |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview.dz-processing .dz-progress { |
||||
opacity: 1; |
||||
-webkit-transition: all 0.2s linear; |
||||
-moz-transition: all 0.2s linear; |
||||
-ms-transition: all 0.2s linear; |
||||
-o-transition: all 0.2s linear; |
||||
transition: all 0.2s linear; |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview.dz-complete .dz-progress { |
||||
opacity: 0; |
||||
-webkit-transition: opacity 0.4s ease-in; |
||||
-moz-transition: opacity 0.4s ease-in; |
||||
-ms-transition: opacity 0.4s ease-in; |
||||
-o-transition: opacity 0.4s ease-in; |
||||
transition: opacity 0.4s ease-in; |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview:not(.dz-processing) .dz-progress { |
||||
-webkit-animation: pulse 6s ease infinite; |
||||
-moz-animation: pulse 6s ease infinite; |
||||
-ms-animation: pulse 6s ease infinite; |
||||
-o-animation: pulse 6s ease infinite; |
||||
animation: pulse 6s ease infinite; |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview .dz-progress { |
||||
opacity: 1; |
||||
z-index: 1000; |
||||
pointer-events: none; |
||||
position: absolute; |
||||
height: 16px; |
||||
left: 50%; |
||||
top: 50%; |
||||
margin-top: -8px; |
||||
width: 80px; |
||||
margin-left: -40px; |
||||
background: rgba(255, 255, 255, 0.9); |
||||
-webkit-transform: scale(1); |
||||
border-radius: 8px; |
||||
overflow: hidden; |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview .dz-progress .dz-upload { |
||||
background: #333; |
||||
background: linear-gradient(to bottom, #666, #444); |
||||
position: absolute; |
||||
top: 0; |
||||
left: 0; |
||||
bottom: 0; |
||||
width: 0; |
||||
-webkit-transition: width 300ms ease-in-out; |
||||
-moz-transition: width 300ms ease-in-out; |
||||
-ms-transition: width 300ms ease-in-out; |
||||
-o-transition: width 300ms ease-in-out; |
||||
transition: width 300ms ease-in-out; |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview.dz-error .dz-error-message { |
||||
display: block; |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview.dz-error:hover .dz-error-message { |
||||
opacity: 1; |
||||
pointer-events: auto; |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview .dz-error-message { |
||||
pointer-events: none; |
||||
z-index: 1000; |
||||
position: absolute; |
||||
display: block; |
||||
display: none; |
||||
opacity: 0; |
||||
-webkit-transition: opacity 0.3s ease; |
||||
-moz-transition: opacity 0.3s ease; |
||||
-ms-transition: opacity 0.3s ease; |
||||
-o-transition: opacity 0.3s ease; |
||||
transition: opacity 0.3s ease; |
||||
border-radius: 8px; |
||||
font-size: 13px; |
||||
top: 130px; |
||||
left: -10px; |
||||
width: 140px; |
||||
background: #be2626; |
||||
background: linear-gradient(to bottom, #be2626, #a92222); |
||||
padding: 0.5em 1.2em; |
||||
color: white; |
||||
} |
||||
|
||||
.dropzone-1 .dz-preview .dz-error-message:after { |
||||
content: ''; |
||||
position: absolute; |
||||
top: -6px; |
||||
left: 64px; |
||||
width: 0; |
||||
height: 0; |
||||
border-left: 6px solid transparent; |
||||
border-right: 6px solid transparent; |
||||
border-bottom: 6px solid #be2626; |
||||
} |
||||
|
||||
// custom styles |
||||
.dropzone-1 { |
||||
border-color: $primary !important; |
||||
border-style: dashed !important; |
||||
align-items: center; |
||||
justify-content: center; |
||||
display: flex; |
||||
background: rgba($primary, 0.1) !important; |
||||
} |
@ -0,0 +1 @@ |
||||
import 'dropzone' |
@ -0,0 +1,49 @@ |
||||
defmodule BlockScoutWeb.AddressContractVerificationViaFlattenedCodeController do |
||||
use BlockScoutWeb, :controller |
||||
|
||||
alias Explorer.Chain.SmartContract |
||||
alias Explorer.SmartContract.{PublisherWorker, Solidity.CodeCompiler, Solidity.CompilerVersion} |
||||
|
||||
def new(conn, %{"address_id" => address_hash_string}) do |
||||
changeset = |
||||
SmartContract.changeset( |
||||
%SmartContract{address_hash: address_hash_string}, |
||||
%{} |
||||
) |
||||
|
||||
compiler_versions = |
||||
case CompilerVersion.fetch_versions() do |
||||
{:ok, compiler_versions} -> |
||||
compiler_versions |
||||
|
||||
{:error, _} -> |
||||
[] |
||||
end |
||||
|
||||
render(conn, "new.html", |
||||
changeset: changeset, |
||||
compiler_versions: compiler_versions, |
||||
evm_versions: CodeCompiler.allowed_evm_versions(), |
||||
address_hash: address_hash_string |
||||
) |
||||
end |
||||
|
||||
def create( |
||||
conn, |
||||
%{ |
||||
"smart_contract" => smart_contract, |
||||
"external_libraries" => external_libraries |
||||
} |
||||
) do |
||||
Que.add(PublisherWorker, {smart_contract["address_hash"], smart_contract, external_libraries, conn}) |
||||
|
||||
send_resp(conn, 204, "") |
||||
end |
||||
|
||||
def parse_optimization_runs(%{"runs" => runs}) do |
||||
case Integer.parse(runs) do |
||||
{integer, ""} -> integer |
||||
_ -> 200 |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,7 @@ |
||||
defmodule BlockScoutWeb.AddressContractVerificationViaJsonController do |
||||
use BlockScoutWeb, :controller |
||||
|
||||
def new(conn, %{"address_id" => address_hash_string}) do |
||||
render(conn, "new.html", address_hash: address_hash_string) |
||||
end |
||||
end |
@ -0,0 +1,314 @@ |
||||
<% 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: "" %> |
||||
<% optimization_runs_value = if metadata_for_verification, do: metadata_for_verification.optimization_runs, else: "200" %> |
||||
<% optimization = if metadata_for_verification, do: metadata_for_verification.optimization, else: true %> |
||||
<% evm_version = if metadata_for_verification, do: metadata_for_verification.evm_version, else: "default" %> |
||||
<% 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: "" %> |
||||
<% fetch_constructor_arguments_automatically = if metadata_for_verification, do: true, else: false %> |
||||
<% display_constructor_arguments_text_area = if fetch_constructor_arguments_automatically, do: "none", else: "block" %> |
||||
<section data-page="contract-verification" class="container new-smart-contract-container"> |
||||
<div data-selector="channel-disconnected-message" style="display: 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 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 %> |
||||
<%= 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. For example, in <span class="tooltip-quote">contract MyContract {..}</span> <strong>MyContract</strong> is the contract name.</div> |
||||
</div> |
||||
</div> |
||||
|
||||
<div class="smart-contract-form-group"> |
||||
<div class="smart-contract-form-group-inner-wrapper"> |
||||
<%= label f, "Include nightly builds" %> |
||||
<div class="center-column"> |
||||
<div class="form-radios-group"> |
||||
<div class="radio-big"> |
||||
<%= radio_button f, :nightly_builds, false, checked: true, class: "form-check-input nightly-builds-false" %> |
||||
<div class="radio-icon"></div> |
||||
<%= label :nightly_builds, :false, gettext("No"), class: "radio-text" %> |
||||
</div> |
||||
<div class="radio-big"> |
||||
<%= radio_button f, :nightly_builds, true, class: "form-check-input nightly-builds-true", "aria-describedby": "nightly_builds-help-block" %> |
||||
<div class="radio-icon"></div> |
||||
<%= label :nightly_builds, :true, gettext("Yes"), class: "radio-text" %> |
||||
</div> |
||||
</div> |
||||
<%= error_tag f, :nightly_builds, id: "nightly_builds-help-block", class: "text-danger form-error" %> |
||||
</div> |
||||
<div class="smart-contract-form-group-tooltip">Select yes if you want to show nightly builds.</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">The compiler version is specified in <span class="tooltip-quote">pragma solidity X.X.X</span>. Use the compiler version rather than the nightly build. If using the Solidity compiler, run <span class="tooltip-quote">solc —version</span> to check.</div> |
||||
</div> |
||||
</div> |
||||
|
||||
<div class="smart-contract-form-group"> |
||||
<div class="smart-contract-form-group-inner-wrapper"> |
||||
<%= label :evm_version, :evm_version, gettext("EVM Version") %> |
||||
<div class="center-column"> |
||||
<%= select f, :evm_version, @evm_versions, class: "form-control border-rounded", selected: evm_version, "aria-describedby": "evm-version-help-block" %> |
||||
</div> |
||||
<div class="smart-contract-form-group-tooltip">The EVM version the contract is written for. If the bytecode does not match the version, we try to verify using the latest EVM version. <a href="https://forum.poa.network/t/smart-contract-verification-evm-version-details/2318" target="_blank">EVM version details</a>.</div> |
||||
</div> |
||||
</div> |
||||
|
||||
<div class="smart-contract-form-group"> |
||||
<div class="smart-contract-form-group-inner-wrapper"> |
||||
<%= label f, "Optimization" %> |
||||
<div class="center-column"> |
||||
<div class="form-radios-group"> |
||||
<div class="radio-big"> |
||||
<%= radio_button f, :optimization, false, checked: !optimization, class: "form-check-input optimization-false" %> |
||||
<div class="radio-icon"></div> |
||||
<%= label :smart_contract_optimization, :false, gettext("No"), class: "radio-text" %> |
||||
</div> |
||||
<div class="radio-big"> |
||||
<%= radio_button f, :optimization, true, checked: optimization, class: "form-check-input optimization-true", "aria-describedby": "optimization-help-block" %> |
||||
<div class="radio-icon"></div> |
||||
<%= label :smart_contract_optimization, :true, gettext("Yes"), class: "radio-text" %> |
||||
</div> |
||||
</div> |
||||
<%= error_tag f, :optimization, id: "optimization-help-block", class: "text-danger form-error" %> |
||||
</div> |
||||
<div class="smart-contract-form-group-tooltip">If you enabled optimization during compilation, select yes.</div> |
||||
</div> |
||||
</div> |
||||
|
||||
<div class="smart-contract-form-group optimization-runs"> |
||||
<div class="smart-contract-form-group-inner-wrapper"> |
||||
<%= label f, :name, gettext("Optimization runs") %> |
||||
<div class="center-column"> |
||||
<%= text_input f, :optimization_runs, value: 200, class: "form-control border-rounded", "aria-describedby": "optimization-runs-help-block", "data-test": "optimization-runs", value: optimization_runs_value %> |
||||
</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 Solidity 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">We recommend using flattened code. This is necessary if your code utilizes a library or inherits dependencies. Use the <a href="https://github.com/poanetwork/solidity-flattener" target="_blank">POA solidity flattener or the <a href="https://www.npmjs.com/package/truffle-flattener" target="_blank">truffle flattener</a>.</div> |
||||
</div> |
||||
</div> |
||||
|
||||
<div class="smart-contract-form-group"> |
||||
<div class="smart-contract-form-group-inner-wrapper"> |
||||
<%= label f, "Try to fetch contructor arguments automatically" %> |
||||
<div class="center-column"> |
||||
<div class="form-radios-group"> |
||||
<div class="radio-big"> |
||||
<%= radio_button f, :autodetect_contructor_args, false, checked: !fetch_constructor_arguments_automatically, class: "form-check-input autodetectfalse" %> |
||||
<div class="radio-icon"></div> |
||||
<%= label :autodetect_contructor_args, :false, gettext("No"), class: "radio-text" %> |
||||
</div> |
||||
<div class="radio-big"> |
||||
<%= radio_button f, :autodetect_contructor_args, true, checked: fetch_constructor_arguments_automatically, class: "form-check-input autodetecttrue", "aria-describedby": "autodetect_contructor_args-help-block" %> |
||||
<div class="radio-icon"></div> |
||||
<%= label :autodetect_contructor_args, :true, gettext("Yes"), class: "radio-text" %> |
||||
</div> |
||||
</div> |
||||
<%= error_tag f, :autodetect_contructor_args, id: "autodetect_contructor_args-help-block", class: "text-danger form-error" %> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
<div class="smart-contract-form-group constructor-arguments" style="display: <%= display_constructor_arguments_text_area %>"> |
||||
<div class="smart-contract-form-group-inner-wrapper"> |
||||
<%= label f, :contructor_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="add-contract-libraries-wrapper"> |
||||
<span class="btn-line js-btn-add-contract-libraries">Add Contract Libraries</span> |
||||
</div> |
||||
|
||||
<div class="smart-contract-libraries-wrapper js-smart-contract-libraries-wrapper"> |
||||
<h2 class="smart-contract-title margin-bottom-md"><%= gettext "Contract Libraries" %></h2> |
||||
|
||||
<div class="contract-library-form-group js-contract-library-form-group active"> |
||||
<div class="smart-contract-form-group"> |
||||
<div class="smart-contract-form-group-inner-wrapper"> |
||||
<%= label :external_libraries, :library1, gettext("Library Name") %> |
||||
<div class="center-column"> |
||||
<%= text_input :external_libraries, :library1_name, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> |
||||
</div> |
||||
<div class="smart-contract-form-group-tooltip">A library name called in the .sol file. Multiple libraries (up to 5) may be added for each contract. Click the Add Library button to add an additional one.</div> |
||||
</div> |
||||
</div> |
||||
|
||||
<div class="smart-contract-form-group"> |
||||
<div class="smart-contract-form-group-inner-wrapper"> |
||||
<%= label :external_libraries, :library1, gettext("Library Address") %> |
||||
<div class="center-column"> |
||||
<%= text_input :external_libraries, :library1_address, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> |
||||
</div> |
||||
<div class="smart-contract-form-group-tooltip">The 0x library address. This can be found in the generated json file or Truffle output (if using truffle).</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
<div class="contract-library-form-group js-contract-library-form-group"> |
||||
<div class="smart-contract-form-group"> |
||||
<div class="smart-contract-form-group-inner-wrapper"> |
||||
<%= label :external_libraries, :library2, gettext("Library Name") %> |
||||
<div class="center-column"> |
||||
<%= text_input :external_libraries, :library2_name, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> |
||||
</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 :external_libraries, :library2, gettext("Library Address") %> |
||||
<div class="center-column"> |
||||
<%= text_input :external_libraries, :library2_address, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> |
||||
</div> |
||||
<div class="smart-contract-form-group-tooltip"></div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
<div class="contract-library-form-group js-contract-library-form-group"> |
||||
<div class="smart-contract-form-group"> |
||||
<div class="smart-contract-form-group-inner-wrapper"> |
||||
<%= label :external_libraries, :library3, gettext("Library Name") %> |
||||
<div class="center-column"> |
||||
<%= text_input :external_libraries, :library3_name, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> |
||||
</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 :external_libraries, :library3, gettext("Library Address") %> |
||||
<div class="center-column"> |
||||
<%= text_input :external_libraries, :library3_address, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> |
||||
</div> |
||||
<div class="smart-contract-form-group-tooltip"></div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
<div class="contract-library-form-group js-contract-library-form-group"> |
||||
<div class="smart-contract-form-group"> |
||||
<div class="smart-contract-form-group-inner-wrapper"> |
||||
<%= label :external_libraries, :library4, gettext("Library Name") %> |
||||
<div class="center-column"> |
||||
<%= text_input :external_libraries, :library4_name, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> |
||||
</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 :external_libraries, :library4, gettext("Library Address") %> |
||||
<div class="center-column"> |
||||
<%= text_input :external_libraries, :library4_address, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> |
||||
</div> |
||||
<div class="smart-contract-form-group-tooltip"></div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
<div class="contract-library-form-group js-contract-library-form-group"> |
||||
<div class="smart-contract-form-group"> |
||||
<div class="smart-contract-form-group-inner-wrapper"> |
||||
<%= label :external_libraries, :library5, gettext("Library Name") %> |
||||
<div class="center-column"> |
||||
<%= text_input :external_libraries, :library5_name, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> |
||||
</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, :library5, gettext("Library Address") %> |
||||
<div class="center-column"> |
||||
<%= text_input f, :library5_address, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> |
||||
</div> |
||||
<div class="smart-contract-form-group-tooltip"></div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
<div class="add-contract-library-wrapper js-add-contract-library-wrapper"> |
||||
<span class="btn-line js-btn-add-contract-library">Add Library</span> |
||||
</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> |
@ -0,0 +1,59 @@ |
||||
<section data-page="contract-verification" class="container new-smart-contract-container"> |
||||
<div data-selector="channel-disconnected-message" style="display: 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 Smart Contract Verification" %></h1> |
||||
|
||||
<div class="smart-contract-form-group"> |
||||
<div class="smart-contract-form-group-inner-wrapper"> |
||||
<label for="smart_contract_address_hash"><%= gettext("Contract Address") %></label> |
||||
<div class="center-column"> |
||||
<input aria-describedby="contract-address-help-block" class="form-control border-rounded" id="smart_contract_address_hash" name="smart_contract[address_hash]" type="text" value=<%= @address_hash %> readonly=""> |
||||
</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 for="smart_contract_metadata_json"><%= gettext("Sources and Metadata JSON") %></label> |
||||
<div class="center-column"> |
||||
<form action="<%= address_contract_verification_path(@conn, :create) %>" class="dropzone-1" id="metadata-json-dropzone" method="post"> |
||||
<button class="dz-message btn-full-primary"><%= gettext("Drop sources and metadata JSON file or click here") %></button> |
||||
</form> |
||||
</div> |
||||
<div class="smart-contract-form-group-tooltip">Drop all Solidity contract source files and JSON metadata file(s) created during contract compilation into the drop zone.</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> |
||||
<button id="verify-via-json-submit" class="btn-full-primary mr-2" data-button-loading="animation"><%= gettext("Verify & publish") %></button> |
||||
<%= 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> |
||||
</div> |
||||
<script defer data-cfasync="false" src="<%= static_path(@conn, "/js/verification-form.js") %>"></script> |
||||
<script defer data-cfasync="false" src="<%= static_path(@conn, "/js/dropzone.min.js") %>"></script> |
||||
</section> |
@ -0,0 +1,5 @@ |
||||
defmodule BlockScoutWeb.AddressContractVerificationViaFlattenedCodeView do |
||||
use BlockScoutWeb, :view |
||||
|
||||
alias Explorer.Chain |
||||
end |
@ -0,0 +1,3 @@ |
||||
defmodule BlockScoutWeb.AddressContractVerificationViaJsonView do |
||||
use BlockScoutWeb, :view |
||||
end |
@ -1,5 +1,3 @@ |
||||
defmodule BlockScoutWeb.AddressContractVerificationView do |
||||
use BlockScoutWeb, :view |
||||
|
||||
alias Explorer.Chain |
||||
end |
||||
|
@ -0,0 +1,63 @@ |
||||
defmodule Explorer.Chain.SmartContractAdditionalSource do |
||||
@moduledoc """ |
||||
The representation of a verified Smart Contract additional sources. |
||||
It is used when contract is verified with Sourcify utility. |
||||
""" |
||||
|
||||
require Logger |
||||
|
||||
use Explorer.Schema |
||||
|
||||
alias Explorer.Chain.{Hash, SmartContract} |
||||
|
||||
@typedoc """ |
||||
* `file_name` - the name of the Solidity file with contract code (with extension). |
||||
* `contract_source_code` - the Solidity source code from the file with `file_name`. |
||||
""" |
||||
|
||||
@type t :: %Explorer.Chain.SmartContractAdditionalSource{ |
||||
file_name: String.t(), |
||||
contract_source_code: String.t() |
||||
} |
||||
|
||||
schema "smart_contracts_additional_sources" do |
||||
field(:file_name, :string) |
||||
field(:contract_source_code, :string) |
||||
|
||||
belongs_to( |
||||
:smart_contract, |
||||
SmartContract, |
||||
foreign_key: :address_hash, |
||||
references: :address_hash, |
||||
type: Hash.Address |
||||
) |
||||
|
||||
timestamps() |
||||
end |
||||
|
||||
def changeset(%__MODULE__{} = smart_contract_additional_source, attrs) do |
||||
smart_contract_additional_source |
||||
|> cast(attrs, [ |
||||
:file_name, |
||||
:contract_source_code, |
||||
:address_hash |
||||
]) |
||||
|> validate_required([:file_name, :contract_source_code, :address_hash]) |
||||
|> unique_constraint(:address_hash) |
||||
end |
||||
|
||||
def invalid_contract_changeset(%__MODULE__{} = smart_contract_additional_source, attrs, error) do |
||||
validated = |
||||
smart_contract_additional_source |
||||
|> cast(attrs, [ |
||||
:file_name, |
||||
:contract_source_code, |
||||
:address_hash |
||||
]) |
||||
|> validate_required([:file_name, :address_hash]) |
||||
|
||||
add_error(validated, :contract_source_code, error_message(error)) |
||||
end |
||||
|
||||
defp error_message(_), do: "There was an error validating your contract, please try again." |
||||
end |
@ -0,0 +1,184 @@ |
||||
defmodule Explorer.ThirdPartyIntegrations.Sourcify do |
||||
@moduledoc """ |
||||
Adapter for contracts verification with https://sourcify.dev/ |
||||
""" |
||||
use Tesla |
||||
|
||||
alias HTTPoison.{Error, Response} |
||||
alias Tesla.Multipart |
||||
|
||||
def check_by_address(address_hash_string) do |
||||
chain_id = config(:chain_id) |
||||
params = [addresses: address_hash_string, chainIds: chain_id] |
||||
http_get_request(check_by_address_url(), params) |
||||
end |
||||
|
||||
def get_metadata(address_hash_string) do |
||||
get_metadata_full_url = get_metadata_url() <> "/" <> address_hash_string |
||||
http_get_request(get_metadata_full_url, []) |
||||
end |
||||
|
||||
def verify(address_hash_string, files) do |
||||
chain_id = config(:chain_id) |
||||
|
||||
multipart_text_params = |
||||
Multipart.new() |
||||
|> Multipart.add_field("chain", chain_id) |
||||
|> Multipart.add_field("address", address_hash_string) |
||||
|
||||
multipart_body = |
||||
files |
||||
|> Enum.reduce(multipart_text_params, fn file, acc -> |
||||
if file do |
||||
acc |
||||
|> Multipart.add_file(file.path, |
||||
name: "files", |
||||
file_name: Path.basename(file.path) |
||||
) |
||||
else |
||||
acc |
||||
end |
||||
end) |
||||
|
||||
http_post_request(verify_url(), multipart_body) |
||||
end |
||||
|
||||
def http_get_request(url, params) do |
||||
request = HTTPoison.get(url, [], params: params) |
||||
|
||||
case request do |
||||
{:ok, %Response{body: body, status_code: 200}} -> |
||||
process_sourcify_response(url, body) |
||||
|
||||
{:ok, %Response{body: body, status_code: status_code}} when status_code in 400..526 -> |
||||
parse_http_error_response(body) |
||||
|
||||
{:ok, %Response{status_code: status_code}} when status_code in 300..308 -> |
||||
{:error, "Sourcify redirected"} |
||||
|
||||
{:ok, %Response{status_code: _status_code}} -> |
||||
{:error, "Sourcify unexpected status code"} |
||||
|
||||
{:error, %Error{reason: reason}} -> |
||||
{:error, reason} |
||||
|
||||
{:error, :nxdomain} -> |
||||
{:error, "Sourcify is not responsive"} |
||||
|
||||
{:error, _} -> |
||||
{:error, "Unexpected response from Sourcify"} |
||||
end |
||||
end |
||||
|
||||
def http_post_request(url, body) do |
||||
request = Tesla.post(url, body) |
||||
|
||||
case request do |
||||
{:ok, %Tesla.Env{body: body}} -> |
||||
process_sourcify_response(url, body) |
||||
|
||||
_ -> |
||||
{:error, "Unexpected response from Sourcify verify method"} |
||||
end |
||||
end |
||||
|
||||
defp process_sourcify_response(url, body) do |
||||
cond do |
||||
url =~ "checkByAddresses" -> |
||||
parse_check_by_address_http_response(body) |
||||
|
||||
url =~ "/verify" -> |
||||
parse_verify_http_response(body) |
||||
|
||||
url =~ "/files/" -> |
||||
parse_get_metadata_http_response(body) |
||||
|
||||
true -> |
||||
{:error, body} |
||||
end |
||||
end |
||||
|
||||
defp parse_verify_http_response(body) do |
||||
body_json = decode_json(body) |
||||
|
||||
case body_json do |
||||
%{"result" => [%{"status" => "perfect"}]} -> |
||||
{:ok, body_json} |
||||
|
||||
%{"result" => [%{"status" => unknown_status}]} -> |
||||
{:error, unknown_status} |
||||
|
||||
body -> |
||||
{:error, body} |
||||
end |
||||
end |
||||
|
||||
defp parse_check_by_address_http_response(body) do |
||||
body_json = decode_json(body) |
||||
|
||||
case body_json do |
||||
[%{"status" => "perfect"}] -> |
||||
{:ok, body_json} |
||||
|
||||
[%{"status" => "false"}] -> |
||||
{:error, "Contract is not verified"} |
||||
|
||||
[%{"status" => unknown_status}] -> |
||||
{:error, unknown_status} |
||||
|
||||
body -> |
||||
{:error, body} |
||||
end |
||||
end |
||||
|
||||
defp parse_get_metadata_http_response(body) do |
||||
body_json = decode_json(body) |
||||
|
||||
case body_json do |
||||
%{"message" => message, "errors" => errors} -> |
||||
{:error, "#{message}: #{decode_json(errors)}"} |
||||
|
||||
metadata -> |
||||
{:ok, metadata} |
||||
end |
||||
end |
||||
|
||||
defp parse_http_error_response(body) do |
||||
body_json = decode_json(body) |
||||
|
||||
if is_map(body_json) do |
||||
{:error, body_json["error"]} |
||||
else |
||||
{:error, body} |
||||
end |
||||
end |
||||
|
||||
def decode_json(data) do |
||||
Jason.decode!(data) |
||||
rescue |
||||
_ -> data |
||||
end |
||||
|
||||
defp config(key) do |
||||
:explorer |
||||
|> Application.get_env(__MODULE__) |
||||
|> Keyword.get(key) |
||||
end |
||||
|
||||
defp base_server_url do |
||||
config(:server_url) |
||||
end |
||||
|
||||
defp verify_url do |
||||
"#{base_server_url()}" <> "/verify" |
||||
end |
||||
|
||||
defp check_by_address_url do |
||||
"#{base_server_url()}" <> "/checkByAddresses" |
||||
end |
||||
|
||||
defp get_metadata_url do |
||||
chain_id = config(:chain_id) |
||||
"#{base_server_url()}" <> "/files/" <> chain_id |
||||
end |
||||
end |
@ -0,0 +1,22 @@ |
||||
defmodule Explorer.Repo.Migrations.SupportSourcify do |
||||
use Ecto.Migration |
||||
|
||||
def change do |
||||
alter table(:smart_contracts) do |
||||
add(:verified_via_sourcify, :boolean, null: true) |
||||
end |
||||
|
||||
create table(:smart_contracts_additional_sources) do |
||||
add(:file_name, :string, null: false) |
||||
add(:contract_source_code, :text, null: false) |
||||
|
||||
add(:address_hash, references(:smart_contracts, column: :address_hash, on_delete: :delete_all, type: :bytea), |
||||
null: false |
||||
) |
||||
|
||||
timestamps() |
||||
end |
||||
|
||||
create(index(:smart_contracts_additional_sources, :address_hash)) |
||||
end |
||||
end |
Loading…
Reference in new issue