diff --git a/.dialyzer-ignore b/.dialyzer-ignore index d171b55576..71f24c3537 100644 --- a/.dialyzer-ignore +++ b/.dialyzer-ignore @@ -23,6 +23,8 @@ 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/block_scout_web/templates/address_contract/index.html.eex:123 +lib/block_scout_web/templates/address_contract/index.html.eex:149 lib/explorer/staking/stake_snapshotting.ex:15: Function do_snapshotting/7 has no local return lib/explorer/staking/stake_snapshotting.ex:147 +lib/explorer/third_party_integrations/sourcify.ex:65 +lib/explorer/third_party_integrations/sourcify.ex:68 diff --git a/apps/block_scout_web/assets/css/_mixins.scss b/apps/block_scout_web/assets/css/_mixins.scss index 8d759a695b..fc88116a37 100644 --- a/apps/block_scout_web/assets/css/_mixins.scss +++ b/apps/block_scout_web/assets/css/_mixins.scss @@ -95,6 +95,7 @@ &:hover { background-color: darken($bg-color, 10%); border-color: darken($bg-color, 10%); + color: $text-color; text-decoration: none; } diff --git a/apps/block_scout_web/assets/css/app.scss b/apps/block_scout_web/assets/css/app.scss index 33ec3631fe..f747b9d96e 100644 --- a/apps/block_scout_web/assets/css/app.scss +++ b/apps/block_scout_web/assets/css/app.scss @@ -119,6 +119,7 @@ $fa-font-path: "~@fortawesome/fontawesome-free/webfonts"; @import "components/_external_link"; @import "components/_label"; @import "components/_token"; +@import "components/_dropzone"; @import "theme/dark-theme"; diff --git a/apps/block_scout_web/assets/css/components/_dropzone.scss b/apps/block_scout_web/assets/css/components/_dropzone.scss new file mode 100644 index 0000000000..694f2a4340 --- /dev/null +++ b/apps/block_scout_web/assets/css/components/_dropzone.scss @@ -0,0 +1,541 @@ +/* + * The MIT License + * Copyright (c) 2012 Matias Meno + */ + + @-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; +} \ No newline at end of file diff --git a/apps/block_scout_web/assets/css/components/_new_smart_contract.scss b/apps/block_scout_web/assets/css/components/_new_smart_contract.scss index 49da607960..9a9925d116 100644 --- a/apps/block_scout_web/assets/css/components/_new_smart_contract.scss +++ b/apps/block_scout_web/assets/css/components/_new_smart_contract.scss @@ -57,6 +57,10 @@ $new-smart-contract-center-column-margin-right: 30px; display: flex; justify-content: flex-start; + &.vertical { + display: block; + } + .radio-big { margin-right: 20px; diff --git a/apps/block_scout_web/assets/js/lib/dropzone.js b/apps/block_scout_web/assets/js/lib/dropzone.js new file mode 100644 index 0000000000..645c4ddfce --- /dev/null +++ b/apps/block_scout_web/assets/js/lib/dropzone.js @@ -0,0 +1 @@ +import 'dropzone' diff --git a/apps/block_scout_web/assets/js/pages/verification_form.js b/apps/block_scout_web/assets/js/pages/verification_form.js index 947a11bb7d..f54f9aaff8 100644 --- a/apps/block_scout_web/assets/js/pages/verification_form.js +++ b/apps/block_scout_web/assets/js/pages/verification_form.js @@ -5,6 +5,7 @@ import humps from 'humps' import { subscribeChannel } from '../socket' import { createStore, connectElements } from '../lib/redux_helpers.js' import '../app' +import Dropzone from 'dropzone' export const initialState = { channelDisconnected: false, @@ -27,7 +28,7 @@ export function reducer (state = initialState, action) { } case 'RECEIVED_VERIFICATION_RESULT': { if (action.msg.verificationResult === 'ok') { - return window.location.replace(window.location.href.split('/contract_verifications')[0] + '/contracts') + return window.location.replace(window.location.href.split('/contract_verifications')[0].split('/verify')[0] + '/contracts') } else { return Object.assign({}, state, { newForm: action.msg.verificationResult @@ -88,6 +89,7 @@ const elements = { } const $contractVerificationPage = $('[data-page="contract-verification"]') +const $contractVerificationChooseTypePage = $('[data-page="contract-verification-choose-type"]') function filterNightlyBuilds (filter) { const select = document.getElementById('smart_contract_compiler_version') @@ -136,6 +138,16 @@ if ($contractVerificationPage.length) { }) $(function () { + if ($('#metadata-json-dropzone').length) { + var dropzone = new Dropzone('#metadata-json-dropzone', { + autoProcessQueue: false, + acceptedFiles: 'text/plain,application/json,.sol,.json', + parallelUploads: 100, + uploadMultiple: true, + addRemoveLinks: true, + params: { address_hash: $('#smart_contract_address_hash').val() } + }) + } setTimeout(function () { $('.nightly-builds-false').trigger('click') }, 10) @@ -188,5 +200,27 @@ if ($contractVerificationPage.length) { $('.js-add-contract-library-wrapper').hide() } }) + + $('#verify-via-json-submit').on('click', function (e) { + if (dropzone.files.length > 0) { + dropzone.processQueue() + } else { + $('#loading').addClass('d-none') + } + }) + }) +} else if ($contractVerificationChooseTypePage.length) { + $('.verify-via-flattened-code').on('click', function () { + if ($(this).prop('checked')) { + $('#verify_via_flattened_code_button').show() + $('#verify_via_json_button').hide() + } + }) + + $('.verify-via-json').on('click', function () { + if ($(this).prop('checked')) { + $('#verify_via_flattened_code_button').hide() + $('#verify_via_json_button').show() + } }) } diff --git a/apps/block_scout_web/assets/package-lock.json b/apps/block_scout_web/assets/package-lock.json index d88757e54c..8314765ec2 100644 --- a/apps/block_scout_web/assets/package-lock.json +++ b/apps/block_scout_web/assets/package-lock.json @@ -16,6 +16,7 @@ "clipboard": "^2.0.4", "core-js": "^2.6.12", "crypto-browserify": "^3.12.0", + "dropzone": "^5.7.2", "eth-net-props": "^1.0.33", "highlight.js": "^10.4.0", "https-browserify": "^1.0.0", @@ -5628,6 +5629,11 @@ "node": ">=8" } }, + "node_modules/dropzone": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/dropzone/-/dropzone-5.9.2.tgz", + "integrity": "sha512-5t2z51DzIsWDbTpwcJIvUlwxBbvcwdCApz0yb9ecKJwG155Xm92KMEZmHW1B0MzoXOKvFwdd0nPu5cpeVcvPHQ==" + }, "node_modules/duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", @@ -23522,6 +23528,11 @@ "is-obj": "^2.0.0" } }, + "dropzone": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/dropzone/-/dropzone-5.9.2.tgz", + "integrity": "sha512-5t2z51DzIsWDbTpwcJIvUlwxBbvcwdCApz0yb9ecKJwG155Xm92KMEZmHW1B0MzoXOKvFwdd0nPu5cpeVcvPHQ==" + }, "duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", diff --git a/apps/block_scout_web/assets/package.json b/apps/block_scout_web/assets/package.json index 1ea07d1557..1877749a01 100644 --- a/apps/block_scout_web/assets/package.json +++ b/apps/block_scout_web/assets/package.json @@ -28,6 +28,7 @@ "clipboard": "^2.0.4", "core-js": "^2.6.12", "crypto-browserify": "^3.12.0", + "dropzone": "^5.7.2", "eth-net-props": "^1.0.33", "highlight.js": "^10.4.0", "https-browserify": "^1.0.0", diff --git a/apps/block_scout_web/assets/webpack.config.js b/apps/block_scout_web/assets/webpack.config.js index d0d8414d43..727e289e79 100644 --- a/apps/block_scout_web/assets/webpack.config.js +++ b/apps/block_scout_web/assets/webpack.config.js @@ -66,6 +66,34 @@ const awesompleteJs = { ] } +const dropzoneJs = { + entry: { + dropzone: './js/lib/dropzone.js', + }, + output: { + filename: '[name].min.js', + path: path.resolve(__dirname, '../priv/static/js') + }, + module: { + rules: [ + { + test: /\.css$/, + use: [ + MiniCssExtractPlugin.loader, + { + loader: "css-loader", + } + ] + } + ] + }, + optimization: { + minimizer: [ + new TerserJSPlugin(jsOptimizationParams), + ] + } +} + const appJs = { entry: { @@ -183,4 +211,4 @@ const appJs = const viewScripts = glob.sync('./js/view_specific/**/*.js').map(transpileViewScript) -module.exports = viewScripts.concat(appJs, awesompleteJs) +module.exports = viewScripts.concat(appJs, awesompleteJs, dropzoneJs) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_controller.ex index 6cea171d5b..fabae4c1a0 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_controller.ex @@ -1,8 +1,12 @@ defmodule BlockScoutWeb.AddressContractVerificationController do use BlockScoutWeb, :controller + alias BlockScoutWeb.API.RPC.ContractController + alias Explorer.Chain + alias Explorer.Chain.Events.Publisher, as: EventsPublisher alias Explorer.Chain.SmartContract alias Explorer.SmartContract.{PublisherWorker, Solidity.CodeCompiler, Solidity.CompilerVersion} + alias Explorer.ThirdPartyIntegrations.Sourcify def new(conn, %{"address_id" => address_hash_string}) do changeset = @@ -40,6 +44,180 @@ defmodule BlockScoutWeb.AddressContractVerificationController do send_resp(conn, 204, "") end + def create( + conn, + %{ + "address_hash" => address_hash_string, + "file" => files + } + ) do + files_array = if is_map(files), do: Enum.map(files, fn {_, file} -> file end), else: [] + + json_files = + files_array + |> Enum.filter(fn file -> file.content_type == "application/json" end) + + json_file = json_files |> Enum.at(0) + + if json_file do + if Chain.smart_contract_verified?(address_hash_string) do + # ... todo: we need to return an error here + EventsPublisher.broadcast( + [{:contract_verification_result, {address_hash_string, {:error, []}, conn}}], + :on_demand + ) + else + case Sourcify.check_by_address(address_hash_string) do + {:ok, _verified_status} -> + get_metadata_and_publish(address_hash_string, conn) + + _ -> + verify_and_publish(address_hash_string, files_array, conn) + end + end + else + EventsPublisher.broadcast( + [{:contract_verification_result, {address_hash_string, {:error, []}, conn}}], + :on_demand + ) + end + + send_resp(conn, 200, "ok") + end + + def create(conn, _params) do + Que.add(PublisherWorker, {"", %{}, %{}, conn}) + + send_resp(conn, 204, "") + end + + defp verify_and_publish(address_hash_string, files_array, conn) do + case Sourcify.verify(address_hash_string, files_array) do + {:ok, _verified_status} -> + case Sourcify.check_by_address(address_hash_string) do + {:ok, _verified_status} -> + get_metadata_and_publish(address_hash_string, conn) + + _ -> + EventsPublisher.broadcast( + [{:contract_verification_result, {address_hash_string, {:error, []}, conn}}], + :on_demand + ) + end + + _ -> + EventsPublisher.broadcast( + [{:contract_verification_result, {address_hash_string, {:error, []}, conn}}], + :on_demand + ) + end + end + + defp get_metadata_and_publish(address_hash_string, conn) do + case Sourcify.get_metadata(address_hash_string) do + {:ok, verification_metadata} -> + %{"params_to_publish" => params_to_publish, "abi" => abi, "secondary_sources" => secondary_sources} = + parse_params_from_sourcify(address_hash_string, verification_metadata) + + ContractController.publish(conn, %{ + "addressHash" => address_hash_string, + "params" => params_to_publish, + "abi" => abi, + "secondarySources" => secondary_sources + }) + + _ -> + EventsPublisher.broadcast( + [{:contract_verification_result, {address_hash_string, {:error, []}, conn}}], + :on_demand + ) + end + end + + def parse_params_from_sourcify(address_hash_string, verification_metadata) do + verification_metadata_json = + verification_metadata + |> Enum.filter(fn %{"name" => name, "content" => _content} -> name =~ ".json" end) + |> Enum.at(0) + + full_params_initial = parse_json_from_sourcify_for_insertion(verification_metadata_json) + + verification_metadata_sol = + verification_metadata + |> Enum.filter(fn %{"name" => name, "content" => _content} -> name =~ ".sol" end) + + full_params = + verification_metadata_sol + |> Enum.reduce(full_params_initial, fn %{"name" => name, "content" => content, "path" => _path} = param, + full_params_acc -> + compilation_target_file_name = Map.get(full_params_acc, "compilation_target_file_name") + + if String.downcase(name) == String.downcase(compilation_target_file_name) do + %{ + "params_to_publish" => extract_primary_source_code(content, Map.get(full_params_acc, "params_to_publish")), + "abi" => Map.get(full_params_acc, "abi"), + "secondary_sources" => Map.get(full_params_acc, "secondary_sources"), + "compilation_target_file_name" => Map.get(full_params_acc, "compilation_target_file_name") + } + else + secondary_sources = [ + prepare_additional_source(address_hash_string, param) | Map.get(full_params_acc, "secondary_sources") + ] + + %{ + "params_to_publish" => Map.get(full_params_acc, "params_to_publish"), + "abi" => Map.get(full_params_acc, "abi"), + "secondary_sources" => secondary_sources, + "compilation_target_file_name" => Map.get(full_params_acc, "compilation_target_file_name") + } + end + end) + + full_params + end + + defp prepare_additional_source(address_hash_string, %{"name" => name, "content" => content, "path" => _path}) do + %{ + "address_hash" => address_hash_string, + "file_name" => name, + "contract_source_code" => content + } + end + + defp extract_primary_source_code(content, params) do + params + |> Map.put("contract_source_code", content) + end + + def parse_json_from_sourcify_for_insertion(verification_metadata_json) do + %{"name" => _, "content" => content} = verification_metadata_json + content_json = Sourcify.decode_json(content) + compiler_version = "v" <> (content_json |> Map.get("compiler") |> Map.get("version")) + abi = content_json |> Map.get("output") |> Map.get("abi") + settings = Map.get(content_json, "settings") + compilation_target_file_path = settings |> Map.get("compilationTarget") |> Map.keys() |> Enum.at(0) + compilation_target_file_name = compilation_target_file_path |> String.split("/") |> Enum.at(-1) + contract_name = settings |> Map.get("compilationTarget") |> Map.get("#{compilation_target_file_path}") + optimizer = Map.get(settings, "optimizer") + + params = + %{} + |> Map.put("name", contract_name) + |> Map.put("compiler_version", compiler_version) + |> Map.put("evm_version", Map.get(settings, "evmVersion")) + |> Map.put("optimization", Map.get(optimizer, "enabled")) + |> Map.put("optimization_runs", Map.get(optimizer, "runs")) + |> Map.put("external_libraries", Map.get(settings, "libraries")) + |> Map.put("verified_via_sourcify", true) + + %{ + "params_to_publish" => params, + "abi" => abi, + "compilation_target_file_name" => compilation_target_file_name, + "secondary_sources" => [] + } + end + def parse_optimization_runs(%{"runs" => runs}) do case Integer.parse(runs) do {integer, ""} -> integer diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_via_flattened_code_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_via_flattened_code_controller.ex new file mode 100644 index 0000000000..239e986919 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_via_flattened_code_controller.ex @@ -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 diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_via_json_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_via_json_controller.ex new file mode 100644 index 0000000000..36159d3cc5 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_contract_verification_via_json_controller.ex @@ -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 diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex index d04a4626b8..d9b19fce6b 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/contract_controller.ex @@ -3,6 +3,7 @@ defmodule BlockScoutWeb.API.RPC.ContractController do alias BlockScoutWeb.API.RPC.Helpers alias Explorer.Chain + alias Explorer.Chain.Events.Publisher, as: EventsPublisher alias Explorer.Chain.SmartContract alias Explorer.SmartContract.Publisher @@ -42,6 +43,27 @@ defmodule BlockScoutWeb.API.RPC.ContractController do end end + def publish(conn, %{"addressHash" => address_hash, "params" => params, "abi" => abi} = input) do + params = + if Map.has_key?(input, "secondarySources") do + params + |> Map.put("secondary_sources", Map.get(input, "secondarySources")) + else + params + end + + result = + case Publisher.publish_smart_contract(address_hash, params, abi) do + {:ok, _contract} = result -> + result + + {:error, changeset} -> + {:error, changeset} + end + + EventsPublisher.broadcast([{:contract_verification_result, {address_hash, result, conn}}], :on_demand) + end + def listcontracts(conn, params) do with pagination_options <- Helpers.put_pagination_options(%{}, params), {:params, {:ok, options}} <- {:params, add_filters(pagination_options, params)} do diff --git a/apps/block_scout_web/lib/block_scout_web/notifier.ex b/apps/block_scout_web/lib/block_scout_web/notifier.ex index 2448e333c3..b74d2f9b1d 100644 --- a/apps/block_scout_web/lib/block_scout_web/notifier.ex +++ b/apps/block_scout_web/lib/block_scout_web/notifier.ex @@ -4,7 +4,13 @@ defmodule BlockScoutWeb.Notifier do """ alias Absinthe.Subscription - alias BlockScoutWeb.{AddressContractVerificationView, Endpoint} + + alias BlockScoutWeb.{ + AddressContractVerificationViaFlattenedCodeView, + AddressContractVerificationViaJsonView, + Endpoint + } + alias Explorer.{Chain, Market, Repo} alias Explorer.Chain.{Address, InternalTransaction, TokenTransfer, Transaction} alias Explorer.Chain.Supply.RSK @@ -40,6 +46,8 @@ defmodule BlockScoutWeb.Notifier do def handle_event( {:chain_event, :contract_verification_result, :on_demand, {address_hash, contract_verification_result, conn}} ) do + verification_from_json_upload? = Map.has_key?(conn.params, "file") + contract_verification_result = case contract_verification_result do {:ok, _} = result -> @@ -55,8 +63,16 @@ defmodule BlockScoutWeb.Notifier do [] end + view = + if verification_from_json_upload? do + AddressContractVerificationViaJsonView + else + AddressContractVerificationViaFlattenedCodeView + end + result = - View.render_to_string(AddressContractVerificationView, "new.html", + view + |> View.render_to_string("new.html", changeset: changeset, compiler_versions: compiler_versions, evm_versions: CodeCompiler.allowed_evm_versions(), diff --git a/apps/block_scout_web/lib/block_scout_web/router.ex b/apps/block_scout_web/lib/block_scout_web/router.ex index ab499a7e1a..00c2682c22 100644 --- a/apps/block_scout_web/lib/block_scout_web/router.ex +++ b/apps/block_scout_web/lib/block_scout_web/router.ex @@ -75,6 +75,28 @@ defmodule BlockScoutWeb.Router do end end + # if path != api_path do + # scope to_string(api_path) <> "/verify_smart_contract" do + # pipe_through(:api) + + # if Application.get_env(:explorer, Explorer.ThirdPartyIntegrations.Sourcify)[:enabled] do + # post("/contract_verifications", BlockScoutWeb.AddressContractVerificationController, :create) + # else + # post("/contract_verifications", BlockScoutWeb.AddressContractVerificationViaFlattenedCodeController, :create) + # end + # end + # else + # scope "/verify_smart_contract" do + # pipe_through(:api) + + # if Application.get_env(:explorer, Explorer.ThirdPartyIntegrations.Sourcify)[:enabled] do + # post("/contract_verifications", BlockScoutWeb.AddressContractVerificationController, :create) + # else + # post("/contract_verifications", BlockScoutWeb.AddressContractVerificationViaFlattenedCodeController, :create) + # end + # end + # end + if Application.get_env(:block_scout_web, WebRouter)[:enabled] do forward("/", BlockScoutWeb.WebRouter) else diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex index b56917b5b6..6efd199e6d 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex @@ -2,6 +2,8 @@ <% minimal_proxy_template = Chain.get_minimal_proxy_template(@address.hash) %> <% metadata_for_verification = minimal_proxy_template || Chain.get_address_verified_twin_contract(@address.hash).verified_contract %> <% smart_contract_verified = BlockScoutWeb.AddressView.smart_contract_verified?(@address) %> +<% additional_sources_from_twin = Chain.get_address_verified_twin_contract(@address.hash).additional_sources %> +<% additional_sources = if smart_contract_verified, do: @address.smart_contract_additional_sources, else: additional_sources_from_twin %>
<%= render BlockScoutWeb.AddressView, "overview.html", assigns %> @@ -24,6 +26,14 @@ <% end %> <%= if smart_contract_verified || (!smart_contract_verified && metadata_for_verification) do %> <% target_contract = if smart_contract_verified, do: @address.smart_contract, else: metadata_for_verification %> + <%= if @address.smart_contract.verified_via_sourcify && smart_contract_verified do %> + + <% end %>
<%= gettext "Contract name:" %>
@@ -73,6 +83,22 @@
+ <%= if target_contract.verified_via_sourcify do %> + <%= Enum.map(additional_sources, fn additional_source -> %> +
+
+

<%= additional_source.file_name %>

+ +
+
+
<%= for {line, number} <- contract_lines_with_index(additional_source.contract_source_code, additional_source.inserted_at) do %>
<%= line %>
<% end %>
+
+
+ <% end)%> + <% end %> +

<%= gettext "Contract ABI" %>

@@ -126,9 +152,16 @@
<% else %> <%= if !smart_contract_verified do %> + <% 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 + %> <%= link( gettext("Verify & Publish"), - to: address_verify_contract_path(@conn, :new, @address.hash), + to: path, class: "button button-primary button-sm float-right ml-3", "data-test": "verify_and_publish" ) %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification/new.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification/new.html.eex index 456b4be90a..c8b34cc212 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification/new.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification/new.html.eex @@ -1,13 +1,4 @@ -<% 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" %> -
+
<%= gettext "Connection Lost" %> @@ -35,362 +26,31 @@
- <%= label f, :name, gettext("Contract Name") %> + <%= label f, "Verification via" %>
- <%= 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" %> -
-
Must match the name specified in the code. For example, in contract MyContract {..} MyContract is the contract name.
-
-
- -
-
- <%= label f, "Include nightly builds" %> -
-
-
- <%= radio_button f, :nightly_builds, false, checked: true, class: "form-check-input nightly-builds-false" %> -
- <%= label :nightly_builds, :false, gettext("No"), class: "radio-text" %> -
-
- <%= radio_button f, :nightly_builds, true, class: "form-check-input nightly-builds-true", "aria-describedby": "nightly_builds-help-block" %> -
- <%= label :nightly_builds, :true, gettext("Yes"), class: "radio-text" %> -
-
- <%= error_tag f, :nightly_builds, id: "nightly_builds-help-block", class: "text-danger form-error" %> -
-
Select yes if you want to show nightly builds.
-
-
- -
-
- <%= label f, :compiler_version, gettext("Compiler") %> -
- <%= 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" %> -
-
The compiler version is specified in pragma solidity X.X.X. Use the compiler version rather than the nightly build. If using the Solidity compiler, run solc —version to check.
-
-
- -
-
- <%= label :evm_version, :evm_version, gettext("EVM Version") %> -
- <%= select f, :evm_version, @evm_versions, class: "form-control border-rounded", selected: evm_version, "aria-describedby": "evm-version-help-block" %> -
-
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. EVM version details.
-
-
- -
-
- <%= label f, "Optimization" %> -
-
-
- <%= radio_button f, :optimization, false, checked: !optimization, class: "form-check-input optimization-false" %> +
+
+ <%= radio_button f, :verify_via, true, checked: true, class: "form-check-input verify-via-flattened-code", "aria-describedby": "verify_via-help-block" %>
- <%= label :smart_contract_optimization, :false, gettext("No"), class: "radio-text" %> + <%= label :verify_via, :true, gettext("Flattened source code"), class: "radio-text" %>
- <%= radio_button f, :optimization, true, checked: optimization, class: "form-check-input optimization-true", "aria-describedby": "optimization-help-block" %> + <%= radio_button f, :verify_via, false, class: "form-check-input verify-via-json" %>
- <%= label :smart_contract_optimization, :true, gettext("Yes"), class: "radio-text" %> + <%= label :verify_via, :false, gettext("Sources and metadata JSON file"), class: "radio-text" %>
- <%= error_tag f, :optimization, id: "optimization-help-block", class: "text-danger form-error" %> -
-
If you enabled optimization during compilation, select yes.
-
-
- -
-
- <%= label f, :name, gettext("Optimization runs") %> -
- <%= 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 %> + <%= error_tag f, :verify_via, id: "verify_via-help-block", class: "text-danger form-error" %>
-
-
-
- -
-
- <%= label f, :contract_source_code, gettext("Enter the Solidity Contract Code") %> -
- <%= 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" %> -
-
We recommend using flattened code. This is necessary if your code utilizes a library or inherits dependencies. Use the POA solidity flattener or the truffle flattener.
-
-
- -
-
- <%= label f, "Try to fetch contructor arguments automatically" %> -
-
-
- <%= radio_button f, :autodetect_contructor_args, false, checked: !fetch_constructor_arguments_automatically, class: "form-check-input autodetectfalse" %> -
- <%= label :autodetect_contructor_args, :false, gettext("No"), class: "radio-text" %> -
-
- <%= radio_button f, :autodetect_contructor_args, true, checked: fetch_constructor_arguments_automatically, class: "form-check-input autodetecttrue", "aria-describedby": "autodetect_contructor_args-help-block" %> -
- <%= label :autodetect_contructor_args, :true, gettext("Yes"), class: "radio-text" %> -
-
- <%= error_tag f, :autodetect_contructor_args, id: "autodetect_contructor_args-help-block", class: "text-danger form-error" %> -
-
-
- -
-
- <%= label f, :contructor_arguments, gettext("ABI-encoded Constructor Arguments (if required by the contract)") %> -
- <%= 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" %> -
-
Add arguments in ABI hex encoded form. Constructor arguments are written right to left, and will be found at the end of the input created bytecode. They may also be parsed here.
-
-
- -
- Add Contract Libraries -
- -
-

<%= gettext "Contract Libraries" %>

- -
-
-
- <%= label :external_libraries, :library1, gettext("Library Name") %> -
- <%= text_input :external_libraries, :library1_name, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> -
-
A library name called in the .sol file. Multiple libraries (up to 10) may be added for each contract. Click the Add Library button to add an additional one.
-
-
- -
-
- <%= label :external_libraries, :library1, gettext("Library Address") %> -
- <%= text_input :external_libraries, :library1_address, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> -
-
The 0x library address. This can be found in the generated json file or Truffle output (if using truffle).
-
-
-
- -
-
-
- <%= label :external_libraries, :library2, gettext("Library Name") %> -
- <%= text_input :external_libraries, :library2_name, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> -
-
-
-
- -
-
- <%= label :external_libraries, :library2, gettext("Library Address") %> -
- <%= text_input :external_libraries, :library2_address, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> -
-
-
-
-
- -
-
-
- <%= label :external_libraries, :library3, gettext("Library Name") %> -
- <%= text_input :external_libraries, :library3_name, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> -
-
-
-
- -
-
- <%= label :external_libraries, :library3, gettext("Library Address") %> -
- <%= text_input :external_libraries, :library3_address, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> -
-
-
-
-
- -
-
-
- <%= label :external_libraries, :library4, gettext("Library Name") %> -
- <%= text_input :external_libraries, :library4_name, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> -
-
-
-
- -
-
- <%= label :external_libraries, :library4, gettext("Library Address") %> -
- <%= text_input :external_libraries, :library4_address, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> -
-
-
-
-
- -
-
-
- <%= label :external_libraries, :library5, gettext("Library Name") %> -
- <%= text_input :external_libraries, :library5_name, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> -
-
-
-
- -
-
- <%= label :external_libraries, :library5, gettext("Library Address") %> -
- <%= text_input :external_libraries, :library5_address, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> -
-
-
-
-
- -
-
-
- <%= label :external_libraries, :library6, gettext("Library Name") %> -
- <%= text_input :external_libraries, :library6_name, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> -
-
-
-
- -
-
- <%= label :external_libraries, :library6, gettext("Library Address") %> -
- <%= text_input :external_libraries, :library6_address, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> -
-
-
-
-
- -
-
-
- <%= label :external_libraries, :library7, gettext("Library Name") %> -
- <%= text_input :external_libraries, :library7_name, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> -
-
-
-
- -
-
- <%= label :external_libraries, :library7, gettext("Library Address") %> -
- <%= text_input :external_libraries, :library7_address, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> -
-
-
-
-
- -
-
-
- <%= label :external_libraries, :library8, gettext("Library Name") %> -
- <%= text_input :external_libraries, :library8_name, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> -
-
-
-
- -
-
- <%= label :external_libraries, :library8, gettext("Library Address") %> -
- <%= text_input :external_libraries, :library8_address, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> -
-
-
-
-
- -
-
-
- <%= label :external_libraries, :library9, gettext("Library Name") %> -
- <%= text_input :external_libraries, :library9_name, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> -
-
-
-
- -
-
- <%= label :external_libraries, :library9, gettext("Library Address") %> -
- <%= text_input :external_libraries, :library9_address, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> -
-
-
-
-
- -
-
-
- <%= label :external_libraries, :library10, gettext("Library Name") %> -
- <%= text_input :external_libraries, :library10_name, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> -
-
-
-
- -
-
- <%= label f, :library10, gettext("Library Address") %> -
- <%= text_input f, :library10_address, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> -
-
-
-
-
- -
- Add Library +
Choose a smart-contract verification method. Currently, Blockscout supports 2 methods:
+ 1. Verification through flattened source code. +
+ 2. Verification through raw source files and standard JSON file(s) with metadata from the Solidity contract compilation process.
@@ -408,8 +68,21 @@ <%= gettext("Loading....") %> - <%= 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("Next"), + to: address_verify_contract_via_flattened_code_path(@conn, :new, @address_hash), + id: "verify_via_flattened_code_button", + class: "btn-full-primary mr-2", + "data-button-loading": "animation" + ) %> + <%= link( + gettext("Next"), + to: address_verify_contract_via_json_path(@conn, :new, @address_hash), + id: "verify_via_json_button", + class: "btn-full-primary mr-2", + style: "display: none;", + "data-button-loading": "animation" + ) %> <%= link( gettext("Cancel"), diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex new file mode 100644 index 0000000000..ab6c3f0554 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex @@ -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" %> +
+ + +
+

<%= gettext "New Smart Contract Verification" %>

+ + <%= form_for @changeset, + address_contract_verification_path(@conn, :create), + [], + fn f -> %> + +
+
+ <%= label f, :address_hash, gettext("Contract Address") %> +
+ <%= 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" %> +
+
The 0x address supplied on contract creation.
+
+
+ +
+
+ <%= label f, :name, gettext("Contract Name") %> +
+ <%= 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" %> +
+
Must match the name specified in the code. For example, in contract MyContract {..} MyContract is the contract name.
+
+
+ +
+
+ <%= label f, "Include nightly builds" %> +
+
+
+ <%= radio_button f, :nightly_builds, false, checked: true, class: "form-check-input nightly-builds-false" %> +
+ <%= label :nightly_builds, :false, gettext("No"), class: "radio-text" %> +
+
+ <%= radio_button f, :nightly_builds, true, class: "form-check-input nightly-builds-true", "aria-describedby": "nightly_builds-help-block" %> +
+ <%= label :nightly_builds, :true, gettext("Yes"), class: "radio-text" %> +
+
+ <%= error_tag f, :nightly_builds, id: "nightly_builds-help-block", class: "text-danger form-error" %> +
+
Select yes if you want to show nightly builds.
+
+
+ +
+
+ <%= label f, :compiler_version, gettext("Compiler") %> +
+ <%= 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" %> +
+
The compiler version is specified in pragma solidity X.X.X. Use the compiler version rather than the nightly build. If using the Solidity compiler, run solc —version to check.
+
+
+ +
+
+ <%= label :evm_version, :evm_version, gettext("EVM Version") %> +
+ <%= select f, :evm_version, @evm_versions, class: "form-control border-rounded", selected: evm_version, "aria-describedby": "evm-version-help-block" %> +
+
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. EVM version details.
+
+
+ +
+
+ <%= label f, "Optimization" %> +
+
+
+ <%= radio_button f, :optimization, false, checked: !optimization, class: "form-check-input optimization-false" %> +
+ <%= label :smart_contract_optimization, :false, gettext("No"), class: "radio-text" %> +
+
+ <%= radio_button f, :optimization, true, checked: optimization, class: "form-check-input optimization-true", "aria-describedby": "optimization-help-block" %> +
+ <%= label :smart_contract_optimization, :true, gettext("Yes"), class: "radio-text" %> +
+
+ <%= error_tag f, :optimization, id: "optimization-help-block", class: "text-danger form-error" %> +
+
If you enabled optimization during compilation, select yes.
+
+
+ +
+
+ <%= label f, :name, gettext("Optimization runs") %> +
+ <%= 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 %> +
+
+
+
+ +
+
+ <%= label f, :contract_source_code, gettext("Enter the Solidity Contract Code") %> +
+ <%= 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" %> +
+
We recommend using flattened code. This is necessary if your code utilizes a library or inherits dependencies. Use the POA solidity flattener or the truffle flattener.
+
+
+ +
+
+ <%= label f, "Try to fetch contructor arguments automatically" %> +
+
+
+ <%= radio_button f, :autodetect_contructor_args, false, checked: !fetch_constructor_arguments_automatically, class: "form-check-input autodetectfalse" %> +
+ <%= label :autodetect_contructor_args, :false, gettext("No"), class: "radio-text" %> +
+
+ <%= radio_button f, :autodetect_contructor_args, true, checked: fetch_constructor_arguments_automatically, class: "form-check-input autodetecttrue", "aria-describedby": "autodetect_contructor_args-help-block" %> +
+ <%= label :autodetect_contructor_args, :true, gettext("Yes"), class: "radio-text" %> +
+
+ <%= error_tag f, :autodetect_contructor_args, id: "autodetect_contructor_args-help-block", class: "text-danger form-error" %> +
+
+
+ +
+
+ <%= label f, :contructor_arguments, gettext("ABI-encoded Constructor Arguments (if required by the contract)") %> +
+ <%= 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" %> +
+
Add arguments in ABI hex encoded form. Constructor arguments are written right to left, and will be found at the end of the input created bytecode. They may also be parsed here.
+
+
+ +
+ Add Contract Libraries +
+ +
+

<%= gettext "Contract Libraries" %>

+ +
+
+
+ <%= label :external_libraries, :library1, gettext("Library Name") %> +
+ <%= text_input :external_libraries, :library1_name, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> +
+
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.
+
+
+ +
+
+ <%= label :external_libraries, :library1, gettext("Library Address") %> +
+ <%= text_input :external_libraries, :library1_address, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> +
+
The 0x library address. This can be found in the generated json file or Truffle output (if using truffle).
+
+
+
+ +
+
+
+ <%= label :external_libraries, :library2, gettext("Library Name") %> +
+ <%= text_input :external_libraries, :library2_name, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> +
+
+
+
+ +
+
+ <%= label :external_libraries, :library2, gettext("Library Address") %> +
+ <%= text_input :external_libraries, :library2_address, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> +
+
+
+
+
+ +
+
+
+ <%= label :external_libraries, :library3, gettext("Library Name") %> +
+ <%= text_input :external_libraries, :library3_name, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> +
+
+
+
+ +
+
+ <%= label :external_libraries, :library3, gettext("Library Address") %> +
+ <%= text_input :external_libraries, :library3_address, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> +
+
+
+
+
+ +
+
+
+ <%= label :external_libraries, :library4, gettext("Library Name") %> +
+ <%= text_input :external_libraries, :library4_name, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> +
+
+
+
+ +
+
+ <%= label :external_libraries, :library4, gettext("Library Address") %> +
+ <%= text_input :external_libraries, :library4_address, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> +
+
+
+
+
+ +
+
+
+ <%= label :external_libraries, :library5, gettext("Library Name") %> +
+ <%= text_input :external_libraries, :library5_name, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> +
+
+
+
+ +
+
+ <%= label f, :library5, gettext("Library Address") %> +
+ <%= text_input f, :library5_address, class: "form-control border-rounded", "aria-describedby": "contract-name-help-block" %> +
+
+
+
+
+ +
+ Add Library +
+
+ +
+ + <%= 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) + ) + %> +
+ <% end %> +
+ +
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex new file mode 100644 index 0000000000..11e0ece9a3 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex @@ -0,0 +1,59 @@ +
+ + +
+

<%= gettext "New Smart Contract Verification" %>

+ +
+
+ +
+ readonly=""> +
+
The 0x address supplied on contract creation.
+
+
+ +
+
+ +
+
+ +
+
+
Drop all Solidity contract source files and JSON metadata file(s) created during contract compilation into the drop zone.
+
+
+
+ + + <%= 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) + ) + %> +
+
+ + +
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_logs/_logs.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_logs/_logs.html.eex index 870c61310b..21ec3c2ef3 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_logs/_logs.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_logs/_logs.html.eex @@ -6,7 +6,14 @@ <%= gettext "To see accurate decoded input data, the contract must be verified." %> <%= case @log.transaction do %> <% %{to_address: %{hash: hash}} -> %> - <%= gettext "Verify the contract " %><%= gettext "here" %> + <% 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 + %> + <%= gettext "Verify the contract " %><%= gettext "here" %> <% _ -> %> <%= nil %> <% end %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex index 65f82e54b3..3c27d3f8e8 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/smart_contract/_functions.html.eex @@ -5,12 +5,19 @@ <%= 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 + %>
<%= render BlockScoutWeb.CommonComponentsView, "_info.html" %> <%= gettext("Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB") %> <%= link( metadata_for_verification.address_hash, to: address_contract_path(@conn, :index, metadata_for_verification.address_hash)) %>.
<%= gettext("All functions displayed below are from ABI of that contract. In order to verify current contract, proceed with") %> <%= link( gettext("Verify & Publish"), - to: address_verify_contract_path(@conn, :new, @address.hash) + to: path ) %> <%= gettext("page") %>
<% end %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_decoded_input.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_decoded_input.html.eex index cb479fc25d..184f647568 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction/_decoded_input.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/_decoded_input.html.eex @@ -8,7 +8,14 @@ <%= gettext "To see accurate decoded input data, the contract must be verified." %> <%= case @transaction do %> <% %{to_address: %{hash: hash}} -> %> - <%= gettext "Verify the contract " %><%= gettext "here" %> + <% 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 + %> + <%= gettext "Verify the contract " %><%= gettext "here" %> <% _ -> %> <%= nil %> <% end %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction_log/_logs.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction_log/_logs.html.eex index 01b479e2bf..b44ae8d919 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction_log/_logs.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction_log/_logs.html.eex @@ -6,7 +6,14 @@ <%= gettext "To see accurate decoded input data, the contract must be verified." %> <%= case @transaction do %> <% %{to_address: %{hash: hash}} -> %> - <%= gettext "Verify the contract " %><%= gettext "here" %> + <% 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 + %> + <%= gettext "Verify the contract " %><%= gettext "here" %> <% _ -> %> <%= nil %> <% end %> diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_via_flattened_code_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_via_flattened_code_view.ex new file mode 100644 index 0000000000..10e9fd7018 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_via_flattened_code_view.ex @@ -0,0 +1,5 @@ +defmodule BlockScoutWeb.AddressContractVerificationViaFlattenedCodeView do + use BlockScoutWeb, :view + + alias Explorer.Chain +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_via_json_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_via_json_view.ex new file mode 100644 index 0000000000..79f8681233 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_via_json_view.ex @@ -0,0 +1,3 @@ +defmodule BlockScoutWeb.AddressContractVerificationViaJsonView do + use BlockScoutWeb, :view +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_view.ex index d757c33343..874b470406 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_contract_verification_view.ex @@ -1,5 +1,3 @@ defmodule BlockScoutWeb.AddressContractVerificationView do use BlockScoutWeb, :view - - alias Explorer.Chain end diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_contract_view.ex index 8608015071..004c06f295 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_contract_view.ex @@ -118,4 +118,11 @@ defmodule BlockScoutWeb.AddressContractView do def contract_creation_code(%Address{contract_code: contract_code}) do {:ok, contract_code} end + + def sourcify_repo_url(address_hash) do + checksummed_hash = Address.checksum(address_hash) + chain_id = Application.get_env(:explorer, Explorer.ThirdPartyIntegrations.Sourcify)[:chain_id] + repo_url = Application.get_env(:explorer, Explorer.ThirdPartyIntegrations.Sourcify)[:repo_url] + repo_url <> chain_id <> "/" <> checksummed_hash <> "/" + end end diff --git a/apps/block_scout_web/lib/block_scout_web/web_router.ex b/apps/block_scout_web/lib/block_scout_web/web_router.ex index 0db15bd1c5..e17fcbbcdd 100644 --- a/apps/block_scout_web/lib/block_scout_web/web_router.ex +++ b/apps/block_scout_web/lib/block_scout_web/web_router.ex @@ -126,6 +126,36 @@ 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, + only: [:new], + as: :verify_contract_via_flattened_code + ) + + resources( + "/verify-via-json", + AddressContractVerificationViaJsonController, + only: [:new], + as: :verify_contract_via_json + ) + resources( "/read-contract", AddressReadContractController, diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot index fb38a21c45..ca869c4df6 100644 --- a/apps/block_scout_web/priv/gettext/default.pot +++ b/apps/block_scout_web/priv/gettext/default.pot @@ -87,7 +87,8 @@ msgid "A string with the name of the module to be invoked." msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:156 +#: +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:156 msgid "ABI-encoded Constructor Arguments (if required by the contract)" msgstr "" @@ -123,7 +124,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:19 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:26 #: lib/block_scout_web/views/address_view.ex:104 msgid "Address" msgstr "" @@ -263,7 +264,9 @@ msgid "Call Code" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:415 +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:88 +#: 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:50 #: 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" @@ -286,17 +289,20 @@ msgid "Clear" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:71 +#: +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:71 msgid "Compiler" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:37 +#: lib/block_scout_web/templates/address_contract/index.html.eex:47 msgid "Compiler version" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:13 +#: 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 msgid "Connection Lost" msgstr "" @@ -324,17 +330,19 @@ msgid "Connection Lost, click to load newer validations" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:56 +#: lib/block_scout_web/templates/address_contract/index.html.eex:66 msgid "Constructor Arguments" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:78 +#: lib/block_scout_web/templates/address_contract/index.html.eex:104 msgid "Contract ABI" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:27 +#: 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 msgid "Contract Address" msgstr "" @@ -347,8 +355,8 @@ msgid "Contract Address Pending" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:109 -#: lib/block_scout_web/templates/address_contract/index.html.eex:117 +#: lib/block_scout_web/templates/address_contract/index.html.eex:135 +#: lib/block_scout_web/templates/address_contract/index.html.eex:143 msgid "Contract Byte Code" msgstr "" @@ -363,32 +371,34 @@ msgid "Contract Creation" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:94 +#: lib/block_scout_web/templates/address_contract/index.html.eex:120 msgid "Contract Creation Code" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:170 +#: +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:170 msgid "Contract Libraries" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:38 +#: +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:38 msgid "Contract Name" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:29 +#: lib/block_scout_web/templates/address_contract/index.html.eex:39 msgid "Contract name:" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:66 +#: lib/block_scout_web/templates/address_contract/index.html.eex:76 msgid "Contract source code" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:100 +#: lib/block_scout_web/templates/address_contract/index.html.eex:126 msgid "Contracts that self destruct in their constructors have no contract code published and cannot be verified." msgstr "" @@ -398,7 +408,7 @@ msgid "Contribute" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:80 +#: lib/block_scout_web/templates/address_contract/index.html.eex:106 msgid "Copy ABI" msgstr "" @@ -411,7 +421,7 @@ msgid "Copy Address" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:96 +#: lib/block_scout_web/templates/address_contract/index.html.eex:122 msgid "Copy Contract Creation Code" msgstr "" @@ -421,7 +431,8 @@ msgid "Copy Decompiled Contract Code" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:68 +#: lib/block_scout_web/templates/address_contract/index.html.eex:78 +#: lib/block_scout_web/templates/address_contract/index.html.eex:92 msgid "Copy Source Code" msgstr "" @@ -468,20 +479,20 @@ msgid "Curl" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_logs/_logs.html.eex:100 +#: lib/block_scout_web/templates/address_logs/_logs.html.eex:107 #: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:7 #: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:20 -#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:103 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:110 msgid "Data" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_logs/_logs.html.eex:31 -#: lib/block_scout_web/templates/address_logs/_logs.html.eex:37 -#: lib/block_scout_web/templates/address_logs/_logs.html.eex:52 -#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:32 -#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:40 -#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:55 +#: 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 msgid "Decoded" msgstr "" @@ -535,7 +546,7 @@ msgid "Details" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:101 +#: lib/block_scout_web/templates/address_contract/index.html.eex:127 msgid "Displaying the init data provided of the creating transaction." msgstr "" @@ -555,8 +566,8 @@ msgid "ERC-721 " msgstr "" #, elixir-format -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:78 -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:113 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:85 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:120 msgid "ETH" msgstr "" @@ -566,8 +577,8 @@ msgid "ETH RPC API Documentation" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:48 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:82 +#: lib/block_scout_web/templates/address_contract/index.html.eex:58 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:82 msgid "EVM Version" msgstr "" @@ -587,7 +598,8 @@ msgid "Emission Reward" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:124 +#: +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:124 msgid "Enter the Solidity Contract Code" msgstr "" @@ -659,18 +671,18 @@ msgid "Nonce" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:151 +#: lib/block_scout_web/templates/address_contract/index.html.eex:184 msgid "External libraries" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:32 +#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:39 msgid "Failed to decode input data." msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_logs/_logs.html.eex:34 -#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:35 +#: lib/block_scout_web/templates/address_logs/_logs.html.eex:41 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:42 msgid "Failed to decode log data." msgstr "" @@ -753,7 +765,7 @@ msgid "However, in general, the" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:18 +#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:25 msgid "IMPORTANT: This information is a best guess based on similar functions from other verified contracts." msgstr "" @@ -911,7 +923,7 @@ msgid "Total Difficulty" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_logs/_logs.html.eex:18 +#: lib/block_scout_web/templates/address_logs/_logs.html.eex:25 #: lib/block_scout_web/views/transaction_view.ex:354 msgid "Transaction" msgstr "" @@ -964,30 +976,22 @@ msgid "Less than" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:185 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:207 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:229 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:251 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:273 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:295 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:317 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:339 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:361 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:383 +#: +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:185 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:207 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:229 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:251 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:273 msgid "Library Address" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:175 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:197 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:219 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:241 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:263 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:285 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:307 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:329 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:351 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:373 +#: +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:175 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:197 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:219 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:241 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:263 msgid "Library Name" msgstr "" @@ -1016,7 +1020,9 @@ msgid "Loading..." msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:409 +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:69 +#: 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:44 msgid "Loading...." msgstr "" @@ -1090,14 +1096,17 @@ msgid "Name" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:18 +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex: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:55 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:98 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:141 +#: +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:55 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:98 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:141 #: lib/block_scout_web/templates/stakes/_rows.html.eex:24 msgid "No" msgstr "" @@ -1109,13 +1118,13 @@ msgid "OUT" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:33 +#: lib/block_scout_web/templates/address_contract/index.html.eex:43 msgid "Optimization enabled" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:42 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:114 +#: lib/block_scout_web/templates/address_contract/index.html.eex:52 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:114 msgid "Optimization runs" msgstr "" @@ -1166,7 +1175,7 @@ msgid "Pending Transactions" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:17 +#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:24 msgid "Potential matches from our contract method database:" msgstr "" @@ -1184,7 +1193,7 @@ msgid "QR Code" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:82 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:89 msgid "Query" msgstr "" @@ -1217,7 +1226,9 @@ msgid "Request URL" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:412 +#: +#: 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:47 msgid "Reset" msgstr "" @@ -1425,7 +1436,7 @@ msgid "This is useful to allow sending requests to blockscout without having to msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:19 +#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:26 msgid "To have guaranteed accuracy, use the link above to verify the contract's source code." msgstr "" @@ -1442,8 +1453,8 @@ msgid "Topic" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_logs/_logs.html.eex:70 -#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:73 +#: lib/block_scout_web/templates/address_logs/_logs.html.eex:77 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:80 msgid "Topics" msgstr "" @@ -1568,22 +1579,24 @@ msgid "Value" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:20 -#: lib/block_scout_web/templates/address_contract/index.html.eex:125 -#: lib/block_scout_web/templates/address_contract/index.html.eex:130 -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:12 +#: lib/block_scout_web/templates/address_contract/index.html.eex:22 +#: lib/block_scout_web/templates/address_contract/index.html.eex:151 +#: lib/block_scout_web/templates/address_contract/index.html.eex:163 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:19 msgid "Verify & Publish" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:411 +#: +#: 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:46 msgid "Verify & publish" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_logs/_logs.html.eex:9 -#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:11 -#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:9 +#: 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 msgid "Verify the contract " msgstr "" @@ -1635,7 +1648,7 @@ msgid "View transaction %{transaction} on %{subnetwork}" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:112 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:119 msgid "WEI" msgstr "" @@ -1650,9 +1663,10 @@ msgid "Wei" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:60 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:103 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:146 +#: +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:60 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:103 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:146 #: lib/block_scout_web/templates/stakes/_rows.html.eex:24 msgid "Yes" msgstr "" @@ -1673,9 +1687,9 @@ msgid "false" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_logs/_logs.html.eex:9 -#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:11 -#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:9 +#: 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 msgid "here" msgstr "" @@ -1761,7 +1775,7 @@ msgid "Loading chart" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:113 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:120 msgid "Log Index" msgstr "" @@ -1924,7 +1938,7 @@ msgid "Waiting for transaction's confirmation..." msgstr "" #, elixir-format -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:82 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:89 msgid "Write" msgstr "" @@ -1950,22 +1964,22 @@ msgid "Search by address, token symbol, name, transaction hash, or block number" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:11 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:18 msgid "All functions displayed below are from ABI of that contract. In order to verify current contract, proceed with" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:20 +#: lib/block_scout_web/templates/address_contract/index.html.eex:22 msgid "All metadata displayed below is from that contract. In order to verify current contract, click" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:20 +#: lib/block_scout_web/templates/address_contract/index.html.eex:22 msgid "button" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:14 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:21 msgid "page" msgstr "" @@ -1976,8 +1990,8 @@ msgid "Play" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:111 -#: lib/block_scout_web/templates/address_contract/index.html.eex:121 +#: lib/block_scout_web/templates/address_contract/index.html.eex:137 +#: lib/block_scout_web/templates/address_contract/index.html.eex:147 msgid "Copy Byte Code" msgstr "" @@ -2597,8 +2611,8 @@ msgid "Stakes" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:18 -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:9 +#: lib/block_scout_web/templates/address_contract/index.html.eex:20 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:16 msgid "Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB" msgstr "" @@ -2754,3 +2768,36 @@ msgstr "" #: lib/block_scout_web/templates/stakes/_stakes_modal_become_candidate.html.eex:16 msgid "Your Pool Short Description (optional)" msgstr "" + +#, elixir-format +#: +#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:26 +msgid "Drop sources and metadata JSON file or click here" +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/address_contract_verification/new.html.eex:72 +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:79 +msgid "Next" +msgstr "" + +#, elixir-format +#: +#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:23 +msgid "Sources and Metadata JSON" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:40 +msgid "Sources and metadata JSON file" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/address_contract/index.html.eex:31 +msgid "This contract has been verified via Sourcify." +msgstr "" diff --git a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po index fb38a21c45..ca869c4df6 100644 --- a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po +++ b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po @@ -87,7 +87,8 @@ msgid "A string with the name of the module to be invoked." msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:156 +#: +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:156 msgid "ABI-encoded Constructor Arguments (if required by the contract)" msgstr "" @@ -123,7 +124,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:19 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:26 #: lib/block_scout_web/views/address_view.ex:104 msgid "Address" msgstr "" @@ -263,7 +264,9 @@ msgid "Call Code" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:415 +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:88 +#: 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:50 #: 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" @@ -286,17 +289,20 @@ msgid "Clear" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:71 +#: +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:71 msgid "Compiler" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:37 +#: lib/block_scout_web/templates/address_contract/index.html.eex:47 msgid "Compiler version" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:13 +#: 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 msgid "Connection Lost" msgstr "" @@ -324,17 +330,19 @@ msgid "Connection Lost, click to load newer validations" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:56 +#: lib/block_scout_web/templates/address_contract/index.html.eex:66 msgid "Constructor Arguments" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:78 +#: lib/block_scout_web/templates/address_contract/index.html.eex:104 msgid "Contract ABI" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:27 +#: 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 msgid "Contract Address" msgstr "" @@ -347,8 +355,8 @@ msgid "Contract Address Pending" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:109 -#: lib/block_scout_web/templates/address_contract/index.html.eex:117 +#: lib/block_scout_web/templates/address_contract/index.html.eex:135 +#: lib/block_scout_web/templates/address_contract/index.html.eex:143 msgid "Contract Byte Code" msgstr "" @@ -363,32 +371,34 @@ msgid "Contract Creation" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:94 +#: lib/block_scout_web/templates/address_contract/index.html.eex:120 msgid "Contract Creation Code" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:170 +#: +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:170 msgid "Contract Libraries" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:38 +#: +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:38 msgid "Contract Name" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:29 +#: lib/block_scout_web/templates/address_contract/index.html.eex:39 msgid "Contract name:" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:66 +#: lib/block_scout_web/templates/address_contract/index.html.eex:76 msgid "Contract source code" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:100 +#: lib/block_scout_web/templates/address_contract/index.html.eex:126 msgid "Contracts that self destruct in their constructors have no contract code published and cannot be verified." msgstr "" @@ -398,7 +408,7 @@ msgid "Contribute" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:80 +#: lib/block_scout_web/templates/address_contract/index.html.eex:106 msgid "Copy ABI" msgstr "" @@ -411,7 +421,7 @@ msgid "Copy Address" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:96 +#: lib/block_scout_web/templates/address_contract/index.html.eex:122 msgid "Copy Contract Creation Code" msgstr "" @@ -421,7 +431,8 @@ msgid "Copy Decompiled Contract Code" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:68 +#: lib/block_scout_web/templates/address_contract/index.html.eex:78 +#: lib/block_scout_web/templates/address_contract/index.html.eex:92 msgid "Copy Source Code" msgstr "" @@ -468,20 +479,20 @@ msgid "Curl" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_logs/_logs.html.eex:100 +#: lib/block_scout_web/templates/address_logs/_logs.html.eex:107 #: lib/block_scout_web/templates/log/_data_decoded_view.html.eex:7 #: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:20 -#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:103 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:110 msgid "Data" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_logs/_logs.html.eex:31 -#: lib/block_scout_web/templates/address_logs/_logs.html.eex:37 -#: lib/block_scout_web/templates/address_logs/_logs.html.eex:52 -#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:32 -#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:40 -#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:55 +#: 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 msgid "Decoded" msgstr "" @@ -535,7 +546,7 @@ msgid "Details" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:101 +#: lib/block_scout_web/templates/address_contract/index.html.eex:127 msgid "Displaying the init data provided of the creating transaction." msgstr "" @@ -555,8 +566,8 @@ msgid "ERC-721 " msgstr "" #, elixir-format -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:78 -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:113 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:85 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:120 msgid "ETH" msgstr "" @@ -566,8 +577,8 @@ msgid "ETH RPC API Documentation" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:48 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:82 +#: lib/block_scout_web/templates/address_contract/index.html.eex:58 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:82 msgid "EVM Version" msgstr "" @@ -587,7 +598,8 @@ msgid "Emission Reward" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:124 +#: +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:124 msgid "Enter the Solidity Contract Code" msgstr "" @@ -659,18 +671,18 @@ msgid "Nonce" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:151 +#: lib/block_scout_web/templates/address_contract/index.html.eex:184 msgid "External libraries" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:32 +#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:39 msgid "Failed to decode input data." msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_logs/_logs.html.eex:34 -#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:35 +#: lib/block_scout_web/templates/address_logs/_logs.html.eex:41 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:42 msgid "Failed to decode log data." msgstr "" @@ -753,7 +765,7 @@ msgid "However, in general, the" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:18 +#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:25 msgid "IMPORTANT: This information is a best guess based on similar functions from other verified contracts." msgstr "" @@ -911,7 +923,7 @@ msgid "Total Difficulty" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_logs/_logs.html.eex:18 +#: lib/block_scout_web/templates/address_logs/_logs.html.eex:25 #: lib/block_scout_web/views/transaction_view.ex:354 msgid "Transaction" msgstr "" @@ -964,30 +976,22 @@ msgid "Less than" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:185 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:207 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:229 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:251 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:273 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:295 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:317 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:339 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:361 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:383 +#: +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:185 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:207 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:229 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:251 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:273 msgid "Library Address" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:175 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:197 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:219 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:241 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:263 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:285 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:307 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:329 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:351 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:373 +#: +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:175 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:197 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:219 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:241 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:263 msgid "Library Name" msgstr "" @@ -1016,7 +1020,9 @@ msgid "Loading..." msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:409 +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:69 +#: 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:44 msgid "Loading...." msgstr "" @@ -1090,14 +1096,17 @@ msgid "Name" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:18 +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex: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:55 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:98 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:141 +#: +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:55 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:98 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:141 #: lib/block_scout_web/templates/stakes/_rows.html.eex:24 msgid "No" msgstr "" @@ -1109,13 +1118,13 @@ msgid "OUT" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:33 +#: lib/block_scout_web/templates/address_contract/index.html.eex:43 msgid "Optimization enabled" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:42 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:114 +#: lib/block_scout_web/templates/address_contract/index.html.eex:52 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:114 msgid "Optimization runs" msgstr "" @@ -1166,7 +1175,7 @@ msgid "Pending Transactions" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:17 +#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:24 msgid "Potential matches from our contract method database:" msgstr "" @@ -1184,7 +1193,7 @@ msgid "QR Code" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:82 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:89 msgid "Query" msgstr "" @@ -1217,7 +1226,9 @@ msgid "Request URL" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:412 +#: +#: 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:47 msgid "Reset" msgstr "" @@ -1425,7 +1436,7 @@ msgid "This is useful to allow sending requests to blockscout without having to msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:19 +#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:26 msgid "To have guaranteed accuracy, use the link above to verify the contract's source code." msgstr "" @@ -1442,8 +1453,8 @@ msgid "Topic" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_logs/_logs.html.eex:70 -#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:73 +#: lib/block_scout_web/templates/address_logs/_logs.html.eex:77 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:80 msgid "Topics" msgstr "" @@ -1568,22 +1579,24 @@ msgid "Value" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:20 -#: lib/block_scout_web/templates/address_contract/index.html.eex:125 -#: lib/block_scout_web/templates/address_contract/index.html.eex:130 -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:12 +#: lib/block_scout_web/templates/address_contract/index.html.eex:22 +#: lib/block_scout_web/templates/address_contract/index.html.eex:151 +#: lib/block_scout_web/templates/address_contract/index.html.eex:163 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:19 msgid "Verify & Publish" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:411 +#: +#: 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:46 msgid "Verify & publish" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_logs/_logs.html.eex:9 -#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:11 -#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:9 +#: 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 msgid "Verify the contract " msgstr "" @@ -1635,7 +1648,7 @@ msgid "View transaction %{transaction} on %{subnetwork}" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:112 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:119 msgid "WEI" msgstr "" @@ -1650,9 +1663,10 @@ msgid "Wei" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:60 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:103 -#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:146 +#: +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:60 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:103 +#: lib/block_scout_web/templates/address_contract_verification_via_flattened_code/new.html.eex:146 #: lib/block_scout_web/templates/stakes/_rows.html.eex:24 msgid "Yes" msgstr "" @@ -1673,9 +1687,9 @@ msgid "false" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_logs/_logs.html.eex:9 -#: lib/block_scout_web/templates/transaction/_decoded_input.html.eex:11 -#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:9 +#: 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 msgid "here" msgstr "" @@ -1761,7 +1775,7 @@ msgid "Loading chart" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:113 +#: lib/block_scout_web/templates/transaction_log/_logs.html.eex:120 msgid "Log Index" msgstr "" @@ -1924,7 +1938,7 @@ msgid "Waiting for transaction's confirmation..." msgstr "" #, elixir-format -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:82 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:89 msgid "Write" msgstr "" @@ -1950,22 +1964,22 @@ msgid "Search by address, token symbol, name, transaction hash, or block number" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:11 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:18 msgid "All functions displayed below are from ABI of that contract. In order to verify current contract, proceed with" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:20 +#: lib/block_scout_web/templates/address_contract/index.html.eex:22 msgid "All metadata displayed below is from that contract. In order to verify current contract, click" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:20 +#: lib/block_scout_web/templates/address_contract/index.html.eex:22 msgid "button" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:14 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:21 msgid "page" msgstr "" @@ -1976,8 +1990,8 @@ msgid "Play" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:111 -#: lib/block_scout_web/templates/address_contract/index.html.eex:121 +#: lib/block_scout_web/templates/address_contract/index.html.eex:137 +#: lib/block_scout_web/templates/address_contract/index.html.eex:147 msgid "Copy Byte Code" msgstr "" @@ -2597,8 +2611,8 @@ msgid "Stakes" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:18 -#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:9 +#: lib/block_scout_web/templates/address_contract/index.html.eex:20 +#: lib/block_scout_web/templates/smart_contract/_functions.html.eex:16 msgid "Contract is not verified. However, we found a verified contract with the same bytecode in Blockscout DB" msgstr "" @@ -2754,3 +2768,36 @@ msgstr "" #: lib/block_scout_web/templates/stakes/_stakes_modal_become_candidate.html.eex:16 msgid "Your Pool Short Description (optional)" msgstr "" + +#, elixir-format +#: +#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:26 +msgid "Drop sources and metadata JSON file or click here" +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/address_contract_verification/new.html.eex:72 +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:79 +msgid "Next" +msgstr "" + +#, elixir-format +#: +#: lib/block_scout_web/templates/address_contract_verification_via_json/new.html.eex:23 +msgid "Sources and Metadata JSON" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:40 +msgid "Sources and metadata JSON file" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/address_contract/index.html.eex:31 +msgid "This contract has been verified via Sourcify." +msgstr "" diff --git a/apps/block_scout_web/test/block_scout_web/features/pages/contract_verify_page.ex b/apps/block_scout_web/test/block_scout_web/features/pages/contract_verify_page.ex index 89a4283efc..f57a723048 100644 --- a/apps/block_scout_web/test/block_scout_web/features/pages/contract_verify_page.ex +++ b/apps/block_scout_web/test/block_scout_web/features/pages/contract_verify_page.ex @@ -6,7 +6,7 @@ defmodule BlockScoutWeb.ContractVerifyPage do import Wallaby.Query def visit_page(session, address_hash) do - visit(session, "/address/#{address_hash}/contract_verifications/new") + visit(session, "/address/#{address_hash}/verify-via-flattened-code/new") end def fill_form(session, %{ diff --git a/apps/explorer/config/config.exs b/apps/explorer/config/config.exs index cbdb3b1879..77d8da48e4 100644 --- a/apps/explorer/config/config.exs +++ b/apps/explorer/config/config.exs @@ -241,6 +241,12 @@ config :explorer, Explorer.Chain.Cache.Uncles, ttl_check_interval: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(1), else: false), global_ttl: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(5)) +config :explorer, Explorer.ThirdPartyIntegrations.Sourcify, + server_url: System.get_env("SOURCIFY_SERVER_URL") || "https://verification.komputing.org/server", + enabled: System.get_env("ENABLE_SOURCIFY_INTEGRATION") == "true", + chain_id: System.get_env("CHAIN_ID"), + repo_url: System.get_env("SOURCIFY_REPO_URL") || "https://contractrepo.komputing.org/contracts/full_match/" + # Import environment specific config. This must remain at the bottom # of this file so it overrides the configuration defined above. import_config "#{Mix.env()}.exs" diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 7ae503aaa5..d370b341dd 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -53,6 +53,7 @@ defmodule Explorer.Chain do Log, PendingBlockOperation, SmartContract, + SmartContractAdditionalSource, StakingPool, StakingPoolsDelegator, Token, @@ -1315,7 +1316,12 @@ defmodule Explorer.Chain do options \\ [], query_decompiled_code_flag \\ false ) do - necessity_by_association = Keyword.get(options, :necessity_by_association, %{}) + necessity_by_association = + options + |> Keyword.get(:necessity_by_association, %{}) + |> Map.merge(%{ + smart_contract_additional_sources: :optional + }) query = from( @@ -3364,7 +3370,7 @@ defmodule Explorer.Chain do naming the address for reference. """ @spec create_smart_contract(map()) :: {:ok, SmartContract.t()} | {:error, Ecto.Changeset.t()} - def create_smart_contract(attrs \\ %{}, external_libraries \\ []) do + def create_smart_contract(attrs \\ %{}, external_libraries \\ [], secondary_sources \\ []) do new_contract = %SmartContract{} smart_contract_changeset = @@ -3372,10 +3378,23 @@ defmodule Explorer.Chain do |> SmartContract.changeset(attrs) |> Changeset.put_change(:external_libraries, external_libraries) + new_contract_additional_source = %SmartContractAdditionalSource{} + + smart_contract_additional_sources_changesets = + if secondary_sources do + secondary_sources + |> Enum.map(fn changeset -> + new_contract_additional_source + |> SmartContractAdditionalSource.changeset(changeset) + end) + else + [] + end + address_hash = Changeset.get_field(smart_contract_changeset, :address_hash) # Enforce ShareLocks tables order (see docs: sharelocks.md) - insert_result = + insert_contract_query = Multi.new() |> Multi.run(:set_address_verified, fn repo, _ -> set_address_verified(repo, address_hash) end) |> Multi.run(:clear_primary_address_names, fn repo, _ -> clear_primary_address_names(repo, address_hash) end) @@ -3384,6 +3403,16 @@ defmodule Explorer.Chain do create_address_name(repo, name, address_hash) end) |> Multi.insert(:smart_contract, smart_contract_changeset) + + insert_contract_query_with_additional_sources = + smart_contract_additional_sources_changesets + |> Enum.with_index() + |> Enum.reduce(insert_contract_query, fn {changeset, index}, multi -> + Multi.insert(multi, "smart_contract_additional_source_#{Integer.to_string(index)}", changeset) + end) + + insert_result = + insert_contract_query_with_additional_sources |> Repo.transaction() case insert_result do @@ -3503,7 +3532,7 @@ defmodule Explorer.Chain do def get_address_verified_twin_contract(address_hash) do case Repo.get(Address, address_hash) do nil -> - %{:verified_contract => nil} + %{:verified_contract => nil, :additional_sources => nil} target_address -> target_address_hash = target_address.hash @@ -3531,12 +3560,15 @@ defmodule Explorer.Chain do verified_contract_twin_query |> Repo.one(timeout: 10_000) + verified_contract_twin_additional_sources = get_contract_additional_sources(verified_contract_twin) + %{ - :verified_contract => verified_contract_twin + :verified_contract => verified_contract_twin, + :additional_sources => verified_contract_twin_additional_sources } _ -> - %{:verified_contract => nil} + %{:verified_contract => nil, :additional_sources => nil} end end end @@ -3587,6 +3619,21 @@ defmodule Explorer.Chain do end end + defp get_contract_additional_sources(verified_contract_twin) do + if verified_contract_twin do + verified_contract_twin_additional_sources_query = + from( + s in SmartContractAdditionalSource, + where: s.address_hash == ^verified_contract_twin.address_hash + ) + + verified_contract_twin_additional_sources_query + |> Repo.all() + else + [] + end + end + @spec address_hash_to_smart_contract(Hash.Address.t()) :: SmartContract.t() | nil def address_hash_to_smart_contract(address_hash) do query = @@ -3612,6 +3659,16 @@ defmodule Explorer.Chain do end end + def smart_contract_verified?(address_hash) do + query = + from( + smart_contract in SmartContract, + where: smart_contract.address_hash == ^address_hash + ) + + if Repo.one(query), do: true, else: false + end + defp fetch_transactions(paging_options \\ nil) do Transaction |> order_by([transaction], desc: transaction.block_number, desc: transaction.index) diff --git a/apps/explorer/lib/explorer/chain/address.ex b/apps/explorer/lib/explorer/chain/address.ex index 712793adcf..693c8474ce 100644 --- a/apps/explorer/lib/explorer/chain/address.ex +++ b/apps/explorer/lib/explorer/chain/address.ex @@ -17,6 +17,7 @@ defmodule Explorer.Chain.Address do Hash, InternalTransaction, SmartContract, + SmartContractAdditionalSource, Token, Transaction, Wei @@ -110,6 +111,7 @@ defmodule Explorer.Chain.Address do has_many(:names, Address.Name, foreign_key: :address_hash) has_many(:decompiled_smart_contracts, DecompiledSmartContract, foreign_key: :address_hash) + has_many(:smart_contract_additional_sources, SmartContractAdditionalSource, foreign_key: :address_hash) timestamps() end diff --git a/apps/explorer/lib/explorer/chain/smart_contract.ex b/apps/explorer/lib/explorer/chain/smart_contract.ex index 72c2d78b2b..3bb3ead1e6 100644 --- a/apps/explorer/lib/explorer/chain/smart_contract.ex +++ b/apps/explorer/lib/explorer/chain/smart_contract.ex @@ -192,6 +192,7 @@ defmodule Explorer.Chain.SmartContract do produce `address` `t:Explorer.Chain.Address.t/0` `contract_code`. * `abi` - The [JSON ABI specification](https://solidity.readthedocs.io/en/develop/abi-spec.html#json) for this contract. + * `verified_via_sourcify` - whether contract verified through Sourcify utility or not. """ @type t :: %Explorer.Chain.SmartContract{ @@ -202,7 +203,8 @@ defmodule Explorer.Chain.SmartContract do constructor_arguments: String.t() | nil, evm_version: String.t() | nil, optimization_runs: non_neg_integer() | nil, - abi: [function_description] + abi: [function_description], + verified_via_sourcify: boolean | nil } schema "smart_contracts" do @@ -215,6 +217,7 @@ defmodule Explorer.Chain.SmartContract do field(:optimization_runs, :integer) embeds_many(:external_libraries, ExternalLibrary) field(:abi, {:array, :map}) + field(:verified_via_sourcify, :boolean) has_many( :decompiled_smart_contracts, @@ -248,7 +251,8 @@ defmodule Explorer.Chain.SmartContract do :abi, :constructor_arguments, :evm_version, - :optimization_runs + :optimization_runs, + :verified_via_sourcify ]) |> validate_required([:name, :compiler_version, :optimization, :contract_source_code, :abi, :address_hash]) |> unique_constraint(:address_hash) @@ -266,7 +270,8 @@ defmodule Explorer.Chain.SmartContract do :address_hash, :evm_version, :optimization_runs, - :constructor_arguments + :constructor_arguments, + :verified_via_sourcify ]) |> validate_required([:name, :compiler_version, :optimization, :address_hash]) diff --git a/apps/explorer/lib/explorer/chain/smart_contract_additional_sources.ex b/apps/explorer/lib/explorer/chain/smart_contract_additional_sources.ex new file mode 100644 index 0000000000..850c28f4b6 --- /dev/null +++ b/apps/explorer/lib/explorer/chain/smart_contract_additional_sources.ex @@ -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 diff --git a/apps/explorer/lib/explorer/smart_contract/publisher.ex b/apps/explorer/lib/explorer/smart_contract/publisher.ex index 8166167eae..22fc5e3c3a 100644 --- a/apps/explorer/lib/explorer/smart_contract/publisher.ex +++ b/apps/explorer/lib/explorer/smart_contract/publisher.ex @@ -45,10 +45,10 @@ defmodule Explorer.SmartContract.Publisher do end end - defp publish_smart_contract(address_hash, params, abi) do + def publish_smart_contract(address_hash, params, abi) do attrs = address_hash |> attributes(params, abi) - Chain.create_smart_contract(attrs, attrs.external_libraries) + Chain.create_smart_contract(attrs, attrs.external_libraries, attrs.secondary_sources) end defp unverified_smart_contract(address_hash, params, error, error_message) do @@ -89,7 +89,9 @@ defmodule Explorer.SmartContract.Publisher do contract_source_code: params["contract_source_code"], constructor_arguments: clean_constructor_arguments, external_libraries: prepared_external_libraries, - abi: abi + secondary_sources: params["secondary_sources"], + abi: abi, + verified_via_sourcify: params["verified_via_sourcify"] } end diff --git a/apps/explorer/lib/explorer/third_party_integrations/sourcify.ex b/apps/explorer/lib/explorer/third_party_integrations/sourcify.ex new file mode 100644 index 0000000000..7a00137067 --- /dev/null +++ b/apps/explorer/lib/explorer/third_party_integrations/sourcify.ex @@ -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 diff --git a/apps/explorer/mix.exs b/apps/explorer/mix.exs index 909dc6d516..d8344ce132 100644 --- a/apps/explorer/mix.exs +++ b/apps/explorer/mix.exs @@ -47,7 +47,8 @@ defmodule Explorer.Mixfile do do: [ :logger, :mix, - :runtime_tools + :runtime_tools, + :tesla ] # Specifies your project dependencies. @@ -106,7 +107,8 @@ defmodule Explorer.Mixfile do {:telemetry, "~> 0.4.1"}, # `Timex.Duration` for `Explorer.Counters.AverageBlockTime.average_block_time/0` {:timex, "~> 3.6"}, - {:con_cache, "~> 0.13"} + {:con_cache, "~> 0.13"}, + {:tesla, "~> 1.3.3"} ] end diff --git a/apps/explorer/priv/repo/migrations/20201214203532_support_sourcify.exs b/apps/explorer/priv/repo/migrations/20201214203532_support_sourcify.exs new file mode 100644 index 0000000000..97854cd8c5 --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20201214203532_support_sourcify.exs @@ -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 diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs index b776473b85..37e5003396 100644 --- a/apps/explorer/test/explorer/chain_test.exs +++ b/apps/explorer/test/explorer/chain_test.exs @@ -3229,7 +3229,12 @@ defmodule Explorer.ChainTest do test "finds a contract address" do address = insert(:address, contract_code: Factory.data("contract_code"), smart_contract: nil, names: []) - |> Repo.preload([:contracts_creation_internal_transaction, :contracts_creation_transaction, :token]) + |> Repo.preload([ + :contracts_creation_internal_transaction, + :contracts_creation_transaction, + :token, + :smart_contract_additional_sources + ]) options = [ necessity_by_association: %{ diff --git a/mix.exs b/mix.exs index ad27371e55..9fff38af1f 100644 --- a/mix.exs +++ b/mix.exs @@ -68,6 +68,7 @@ defmodule BlockScout.Mixfile do defp deps do [ {:absinthe_plug, git: "https://github.com/blockscout/absinthe_plug.git", tag: "1.5.3", override: true}, + {:tesla, "~> 1.3.3"}, # Documentation {:ex_doc, "~> 0.19.0", only: [:dev]}, {:number, "~> 1.0.3"}