diff --git a/.circleci/config.yml b/.circleci/config.yml index ef25bc232e..13f7677a19 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -40,6 +40,20 @@ jobs: - v6-npm-install-{{ .Branch }} - v6-npm-install + - run: + command: npm install + working_directory: "apps/explorer" + + - save_cache: + key: v3-npm-install-{{ .Branch }}-{{ checksum "apps/explorer/package-lock.json" }} + paths: "apps/explorer/node_modules" + - save_cache: + key: v3-npm-install-{{ .Branch }} + paths: "apps/explorer/node_modules" + - save_cache: + key: v3-npm-install + paths: "apps/explorer/node_modules" + - run: command: npm install working_directory: "apps/explorer_web/assets" @@ -101,7 +115,6 @@ jobs: - mix.exs - mix.lock - appspec.yml - check_formatted: docker: # Ensure .tool-versions matches @@ -300,14 +313,6 @@ jobs: name: Wait for DB command: dockerize -wait tcp://localhost:5432 -timeout 1m - - run: - name: Install 'solc' - command: | - echo 'deb http://ppa.launchpad.net/ethereum/ethereum/ubuntu trusty main' | sudo tee /etc/apt/sources.list.d/pgdg.list > /dev/null - sudo apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 923F6CA9 - sudo apt-get update - sudo apt-get install solc - - run: mix coveralls.circle --umbrella - store_test_results: diff --git a/.gitignore b/.gitignore index 4ad6bb64bc..07acc7d3e1 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ npm-debug.log # Static artifacts /apps/explorer_web/assets/node_modules +/apps/explorer/node_modules # Since we are building assets from assets/, # we ignore priv/static. You may want to comment diff --git a/README.md b/README.md index fff08fc8e5..b6644e4dcd 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,9 @@ To get POA Explorer up and running locally: * Set up some default configuration with: `cp apps/explorer_web/config/dev.secret.exs.example apps/explorer_web/config/dev.secret.exs` * Install dependencies with `mix do deps.get, local.rebar, deps.compile, compile` * Create and migrate your database with `mix ecto.create && mix ecto.migrate` - * Install Node.js dependencies with `cd apps/explorer_web/assets && npm install; cd -` + * Install Node.js dependencies with: + * `cd apps/explorer_web/assets && npm install; cd -` + * `cd apps/explorer && npm install; cd -` * Start Phoenix with `mix phx.server` Now you can visit [`localhost:4000`](http://localhost:4000) from your browser. @@ -60,7 +62,6 @@ Configure your local CCMenu with the following url: [`https://circleci.com/gh/po #### Prerequisites * PhantomJS (for wallaby) - * `Solidity` - http://solidity.readthedocs.io/en/v0.4.24/installing-solidity.html #### Running the tests diff --git a/apps/explorer/lib/explorer/smart_contract/publisher.ex b/apps/explorer/lib/explorer/smart_contract/publisher.ex index c6e2f9d326..f66d567c17 100644 --- a/apps/explorer/lib/explorer/smart_contract/publisher.ex +++ b/apps/explorer/lib/explorer/smart_contract/publisher.ex @@ -14,7 +14,7 @@ defmodule Explorer.SmartContract.Publisher do Explorer.SmartContract.Publisher.publish( "0x0f95fa9bc0383e699325f2658d04e8d96d87b90c", %{ - "compiler" => "0.4.24", + "compiler_version" => "0.4.24", "contract_source_code" => "pragma solidity ^0.4.24; contract SimpleStorage { uint storedData; function set(uint x) public { storedData = x; } function get() public constant returns (uint) { return storedData; } }", "name" => "SimpleStorage", "optimization" => false @@ -55,7 +55,7 @@ defmodule Explorer.SmartContract.Publisher do %{ address_hash: address_hash, name: params["name"], - compiler_version: params["compiler"], + compiler_version: params["compiler_version"], optimization: params["optimization"], contract_source_code: params["contract_source_code"], abi: abi diff --git a/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex b/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex index 0c2c3b349a..4b282e9d70 100644 --- a/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex +++ b/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex @@ -3,102 +3,116 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do Module responsible to compile the Solidity code of a given Smart Contract. """ - @doc ~S""" + @doc """ Compiles a code in the solidity command line. Returns a `Map`. ## Examples - iex(1)> Explorer.SmartContract.Solidity.CodeCompiler.run("SimpleStorage", "pragma solidity ^0.4.23; contract SimpleStorage {uint storedData; function set(uint x) public {storedData = x; } function get() public constant returns (uint) {return storedData; } }", false) - %{ - "contracts" => %{ - "SimpleStorage" => %{ - "SimpleStorage" => %{ - "abi" => [ - %{ - "constant" => false, - "inputs" => [%{"name" => "x", "type" => "uint256"}], - "name" => "set", - "outputs" => [], - "payable" => false, - "stateMutability" => "nonpayable", - "type" => "function" - }, - %{ - "constant" => true, - "inputs" => [], - "name" => "get", - "outputs" => [%{"name" => "", "type" => "uint256"}], - "payable" => false, - "stateMutability" => "view", - "type" => "function" - } - ], - "evm" => %{ - "bytecode" => %{ - "linkReferences" => %{}, - "object" => "608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a72305820017172d01c000255d5c74c0efce764adf7c4ae444d7f7e2ed852f6fb9b73df5d0029", - "opcodes" => "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0x10 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0xDF DUP1 PUSH2 0x1F PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH1 0x49 JUMPI PUSH1 0x0 CALLDATALOAD PUSH29 0x100000000000000000000000000000000000000000000000000000000 SWAP1 DIV PUSH4 0xFFFFFFFF AND DUP1 PUSH4 0x60FE47B1 EQ PUSH1 0x4E JUMPI DUP1 PUSH4 0x6D4CE63C EQ PUSH1 0x78 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH1 0x59 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x76 PUSH1 0x4 DUP1 CALLDATASIZE SUB DUP2 ADD SWAP1 DUP1 DUP1 CALLDATALOAD SWAP1 PUSH1 0x20 ADD SWAP1 SWAP3 SWAP2 SWAP1 POP POP POP PUSH1 0xA0 JUMP JUMPDEST STOP JUMPDEST CALLVALUE DUP1 ISZERO PUSH1 0x83 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x8A PUSH1 0xAA JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 DUP3 DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST DUP1 PUSH1 0x0 DUP2 SWAP1 SSTORE POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD SWAP1 POP SWAP1 JUMP STOP LOG1 PUSH6 0x627A7A723058 KECCAK256 ADD PUSH18 0x72D01C000255D5C74C0EFCE764ADF7C4AE44 0x4d PUSH32 0x7E2ED852F6FB9B73DF5D00290000000000000000000000000000000000000000 ", - "sourceMap" => "25:157:0:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;25:157:0;;;;;;;" - }, - "deployedBytecode" => %{ - "linkReferences" => %{}, - "object" => "6080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a72305820017172d01c000255d5c74c0efce764adf7c4ae444d7f7e2ed852f6fb9b73df5d0029", - "opcodes" => "PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH1 0x49 JUMPI PUSH1 0x0 CALLDATALOAD PUSH29 0x100000000000000000000000000000000000000000000000000000000 SWAP1 DIV PUSH4 0xFFFFFFFF AND DUP1 PUSH4 0x60FE47B1 EQ PUSH1 0x4E JUMPI DUP1 PUSH4 0x6D4CE63C EQ PUSH1 0x78 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH1 0x59 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x76 PUSH1 0x4 DUP1 CALLDATASIZE SUB DUP2 ADD SWAP1 DUP1 DUP1 CALLDATALOAD SWAP1 PUSH1 0x20 ADD SWAP1 SWAP3 SWAP2 SWAP1 POP POP POP PUSH1 0xA0 JUMP JUMPDEST STOP JUMPDEST CALLVALUE DUP1 ISZERO PUSH1 0x83 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x8A PUSH1 0xAA JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 DUP3 DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST DUP1 PUSH1 0x0 DUP2 SWAP1 SSTORE POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD SWAP1 POP SWAP1 JUMP STOP LOG1 PUSH6 0x627A7A723058 KECCAK256 ADD PUSH18 0x72D01C000255D5C74C0EFCE764ADF7C4AE44 0x4d PUSH32 0x7E2ED852F6FB9B73DF5D00290000000000000000000000000000000000000000 ", - "sourceMap" => "25:157:0:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;66:46;;8:9:-1;5:2;;;30:1;27;20:12;5:2;66:46:0;;;;;;;;;;;;;;;;;;;;;;;;;;113:67;;8:9:-1;5:2;;;30:1;27;20:12;5:2;113:67:0;;;;;;;;;;;;;;;;;;;;;;;66:46;108:1;95:10;:14;;;;66:46;:::o;113:67::-;153:4;167:10;;160:17;;113:67;:::o" - }, - "gasEstimates" => %{ - "creation" => %{ - "codeDepositCost" => "44600", - "executionCost" => "93", - "totalCost" => "44693" - }, - "external" => %{"get()" => "424", "set(uint256)" => "20205"} - } - }, - "metadata" => "{\"compiler\":{\"version\":\"0.4.24+commit.e67f0147\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"constant\":false,\"inputs\":[{\"name\":\"x\",\"type\":\"uint256\"}],\"name\":\"set\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"methods\":{}},\"userdoc\":{\"methods\":{}}},\"settings\":{\"compilationTarget\":{\"SimpleStorage\":\"SimpleStorage\"},\"evmVersion\":\"byzantium\",\"libraries\":{},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[]},\"sources\":{\"SimpleStorage\":{\"keccak256\":\"0x3f5ecc4c6077dffdfc98d9781295205833bf0558bc2a0c86fc3d5f246808ba34\",\"urls\":[\"bzzr://60d588b13340f26a038f51934f9b8c3cf3928372bc4358848a86960040a3a8e2\"]}},\"version\":1}" + + iex(1)> Explorer.SmartContract.Solidity.CodeCompiler.run( + ...> "SimpleStorage", + ...> "v0.4.24+commit.e67f0147", + ...> \""" + ...> pragma solidity ^0.4.24; + ...> + ...> contract SimpleStorage { + ...> uint storedData; + ...> + ...> function set(uint x) public { + ...> storedData = x; + ...> } + ...> + ...> function get() public constant returns (uint) { + ...> return storedData; + ...> } + ...> } + ...> \""", + ...> false + ...> ) + { + :ok, + %{ + "abi" => [ + %{ + "constant" => false, + "inputs" => [%{"name" => "x", "type" => "uint256"}], + "name" => "set", + "outputs" => [], + "payable" => false, + "stateMutability" => "nonpayable", + "type" => "function" + }, + %{ + "constant" => true, + "inputs" => [], + "name" => "get", + "outputs" => [%{"name" => "", "type" => "uint256"}], + "payable" => false, + "stateMutability" => "view", + "type" => "function" } - } - }, - "sources" => %{"SimpleStorage" => %{"id" => 0}} + ], + "bytecode" => "6080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a72305820235fceab083d33bf112b473c85551306a29f32dcdc7e95b4dfdd697c1db188ec0029", + "name" => "SimpleStorage", + "opcodes" => "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0x10 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0xDF DUP1 PUSH2 0x1F PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH1 0x49 JUMPI PUSH1 0x0 CALLDATALOAD PUSH29 0x100000000000000000000000000000000000000000000000000000000 SWAP1 DIV PUSH4 0xFFFFFFFF AND DUP1 PUSH4 0x60FE47B1 EQ PUSH1 0x4E JUMPI DUP1 PUSH4 0x6D4CE63C EQ PUSH1 0x78 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH1 0x59 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x76 PUSH1 0x4 DUP1 CALLDATASIZE SUB DUP2 ADD SWAP1 DUP1 DUP1 CALLDATALOAD SWAP1 PUSH1 0x20 ADD SWAP1 SWAP3 SWAP2 SWAP1 POP POP POP PUSH1 0xA0 JUMP JUMPDEST STOP JUMPDEST CALLVALUE DUP1 ISZERO PUSH1 0x83 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x8A PUSH1 0xAA JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 DUP3 DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST DUP1 PUSH1 0x0 DUP2 SWAP1 SSTORE POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD SWAP1 POP SWAP1 JUMP STOP LOG1 PUSH6 0x627A7A723058 KECCAK256 0x23 0x5f 0xce 0xab ADDMOD RETURNDATASIZE CALLER 0xbf GT 0x2b 0x47 EXTCODECOPY DUP6 SSTORE SGT MOD LOG2 SWAP16 ORIGIN 0xdc 0xdc PUSH31 0x95B4DFDD697C1DB188EC002900000000000000000000000000000000000000 " + } } - """ - def run(name, code, optimization) do + def run(name, compiler_version, code, optimize) do {response, _status} = System.cmd( - Application.app_dir(:explorer, "priv/solc.bash"), - [generate_settings(name, code, optimization)] + "node", + [ + Application.app_dir(:explorer, "priv/compile_solc.js"), + code, + compiler_version, + optimize_value(optimize) + ] ) - Jason.decode!(response) - end - - @doc """ - For more output options check the documentation. - https://solidity.readthedocs.io/en/v0.4.24/using-the-compiler.html#compiler-input-and-output-json-description - """ - def generate_settings(name, code, optimization) do - """ - { - "language": "Solidity", - "sources": { - "#{name}": - { - "content": "#{code}" - } - }, - "settings": { - "optimizer": { - "enabled": #{optimization} - }, - "outputSelection": { - "*": { - "*": [ "evm.bytecode", "evm.deployedBytecode", "evm.gasEstimates", "abi", "metadata" ] - } + with %{"contracts" => contracts} <- Jason.decode!(response), + %{"interface" => abi, "runtimeBytecode" => bytecode, "opcodes" => opcodes} <- + get_contract_info(contracts, name) do + { + :ok, + %{ + "abi" => Jason.decode!(abi), + "bytecode" => bytecode, + "name" => name, + "opcodes" => opcodes } } - } - """ + else + error -> + parse_error(error) + end end + + def get_contract_info(contracts, _) when contracts == %{}, do: %{"errors" => []} + + def get_contract_info(contracts, name) do + new_versions_name = ":" <> name + + case contracts do + %{^new_versions_name => response} -> + response + + %{^name => response} -> + response + + _ -> + {:error, :name} + end + end + + def parse_error({:error, :name} = error), do: error + def parse_error(%{"error" => error}), do: {:error, [error]} + def parse_error(%{"errors" => errors}), do: {:error, errors} + + defp optimize_value(false), do: "0" + defp optimize_value("false"), do: "0" + + defp optimize_value(true), do: "1" + defp optimize_value("true"), do: "1" end diff --git a/apps/explorer/lib/explorer/smart_contract/solidity/compiler_version.ex b/apps/explorer/lib/explorer/smart_contract/solidity/compiler_version.ex index 20c404785a..9096a405de 100644 --- a/apps/explorer/lib/explorer/smart_contract/solidity/compiler_version.ex +++ b/apps/explorer/lib/explorer/smart_contract/solidity/compiler_version.ex @@ -3,6 +3,8 @@ defmodule Explorer.SmartContract.Solidity.CompilerVersion do Adapter for fetching compiler versions from https://solc-bin.ethereum.org/bin/list.json. """ + @unsupported_versions ~w(0.1.1 0.1.2) + @doc """ Fetches a list of compilers from the Ethereum Solidity API. """ @@ -23,16 +25,30 @@ defmodule Explorer.SmartContract.Solidity.CompilerVersion do end defp format_data(json) do - {:ok, releases} = + versions = json |> Jason.decode!() - |> Map.fetch("releases") + |> Map.fetch!("builds") + |> remove_unsupported_versions() + |> format_versions() + |> Enum.reverse() + + ["latest" | versions] + end + + defp remove_unsupported_versions(builds) do + Enum.reject(builds, fn %{"version" => version} -> + Enum.member?(@unsupported_versions, version) + end) + end - releases - |> Map.to_list() - |> Enum.map(fn {key, _value} -> {key, key} end) - |> Enum.sort() - |> Enum.reverse() + defp format_versions(builds) do + Enum.map(builds, fn build -> + build + |> Map.fetch!("path") + |> String.replace_prefix("soljson-", "") + |> String.replace_suffix(".js", "") + end) end defp decode_json(json) do diff --git a/apps/explorer/lib/explorer/smart_contract/verifier.ex b/apps/explorer/lib/explorer/smart_contract/verifier.ex index f119b851d0..ca1384b67b 100644 --- a/apps/explorer/lib/explorer/smart_contract/verifier.ex +++ b/apps/explorer/lib/explorer/smart_contract/verifier.ex @@ -11,32 +11,36 @@ defmodule Explorer.SmartContract.Verifier do alias Explorer.SmartContract.Solidity.CodeCompiler def evaluate_authenticity(_, %{"name" => ""}), do: {:error, :name} - def evaluate_authenticity(_, %{"contract_source_code" => ""}), do: {:error, :contract_source_code} + + def evaluate_authenticity(_, %{"contract_source_code" => ""}), + do: {:error, :contract_source_code} def evaluate_authenticity(address_hash, %{ "name" => name, "contract_source_code" => contract_source_code, - "optimization" => optimization + "optimization" => optimization, + "compiler_version" => compiler_version }) do - solc_output = CodeCompiler.run(name, contract_source_code, optimization) + solc_output = CodeCompiler.run(name, compiler_version, contract_source_code, optimization) + + compare_bytecodes(solc_output, address_hash) + end + + defp compare_bytecodes({:error, :name}, _), do: {:error, :name} + defp compare_bytecodes({:error, _}, _), do: {:error, :compilation} - case solc_output do - %{ - "contracts" => %{ - ^name => %{ - ^name => %{ - "abi" => abi, - "evm" => %{ - "bytecode" => %{"object" => bytecode} - } - } - } - } - } -> - compare_bytecodes(address_hash, abi, bytecode) + defp compare_bytecodes({:ok, %{"abi" => abi, "bytecode" => bytecode}}, address_hash) do + generated_bytecode = extract_bytecode(bytecode) + + "0x" <> blockchain_bytecode = + address_hash + |> Chain.smart_contract_bytecode() + |> extract_bytecode - _ -> - {:error, :compilation} + if generated_bytecode == blockchain_bytecode do + {:ok, %{abi: abi}} + else + {:error, :generated_bytecode} end end @@ -56,19 +60,4 @@ defmodule Explorer.SmartContract.Verifier do bytecode end - - defp compare_bytecodes(address_hash, abi, bytecode) do - generated_bytecode = extract_bytecode(bytecode) - - "0x" <> blockchain_bytecode = - address_hash - |> Chain.smart_contract_bytecode() - |> extract_bytecode - - if generated_bytecode == blockchain_bytecode do - {:ok, %{abi: abi}} - else - {:error, :generated_bytecode} - end - end end diff --git a/apps/explorer/package.json b/apps/explorer/package.json new file mode 100644 index 0000000000..74a583763c --- /dev/null +++ b/apps/explorer/package.json @@ -0,0 +1,18 @@ +{ + "repository": { + "type": "git", + "url": "git+https://github.com/poanetwork/poa-explorer.git" + }, + "private": true, + "name": "poa-explorer", + "author": "POA Network", + "license": "GPL-3.0", + "engines": { + "node": "9.x", + "npm": "5.x" + }, + "scripts": {}, + "dependencies": { + "solc-js": "^0.4.20-browser.1" + } +} diff --git a/apps/explorer/priv/compile_solc.js b/apps/explorer/priv/compile_solc.js new file mode 100755 index 0000000000..55c62194e9 --- /dev/null +++ b/apps/explorer/priv/compile_solc.js @@ -0,0 +1,15 @@ +#!/usr/bin/env node + +const solc = require('solc-js'); + +var sourceCode = process.argv[2]; +var version = process.argv[3]; +var optimize = process.argv[4]; + +var compiled_code = solc.loadRemoteVersion(version, function (err, solcSnapshot) { + if (err) { + console.log(JSON.stringify(err)); + } else { + console.log(JSON.stringify(solcSnapshot.compile(sourceCode, optimize))); + } +}); diff --git a/apps/explorer/priv/solc.bash b/apps/explorer/priv/solc.bash deleted file mode 100755 index 1350298daa..0000000000 --- a/apps/explorer/priv/solc.bash +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -echo "$1" | solc --standard-json diff --git a/apps/explorer/test/explorer/smart_contract/publisher_test.exs b/apps/explorer/test/explorer/smart_contract/publisher_test.exs index b81bfd1ccd..9f219843b0 100644 --- a/apps/explorer/test/explorer/smart_contract/publisher_test.exs +++ b/apps/explorer/test/explorer/smart_contract/publisher_test.exs @@ -5,43 +5,29 @@ defmodule Explorer.SmartContract.PublisherTest do doctest Explorer.SmartContract.Publisher - alias Explorer.Chain.{SmartContract, Hash} + alias Explorer.Chain.SmartContract alias Explorer.SmartContract.Publisher + alias Explorer.Factory describe "publish/2" do test "with valid data creates a smart_contract" do - address_hash = "0x0f95fa9bc0383e699325f2658d04e8d96d87b90c" + contract_code_info = Factory.contract_code_info() - smart_contract_bytecode = - "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582040d82a7379b1ee1632ad4d8a239954fd940277b25628ead95259a85c5eddb2120029" - - created_contract_address = insert(:address, hash: address_hash, contract_code: smart_contract_bytecode) - - transaction = - :transaction - |> insert() - |> with_block() - - insert( - :internal_transaction_create, - transaction: transaction, - index: 0, - created_contract_address: created_contract_address, - created_contract_code: smart_contract_bytecode - ) + contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) valid_attrs = %{ - "contract_source_code" => - "pragma solidity ^0.4.23;\r\n\r\ncontract SimpleStorage {\r\n uint storedData;\r\n\r\n function set(uint x) public {\r\n storedData = x;\r\n }\r\n\r\n function get() public constant returns (uint) {\r\n return storedData;\r\n }\r\n}", - "compiler" => "0.4.24", - "name" => "SimpleStorage", - "optimization" => false + "contract_source_code" => contract_code_info.source_code, + "compiler_version" => contract_code_info.version, + "name" => contract_code_info.name, + "optimization" => contract_code_info.optimized } - assert {:ok, %SmartContract{} = smart_contract} = Publisher.publish(address_hash, valid_attrs) + response = Publisher.publish(contract_address.hash, valid_attrs) + assert {:ok, %SmartContract{} = smart_contract} = response + + assert smart_contract.address_hash == contract_address.hash assert smart_contract.name == valid_attrs["name"] - assert Hash.to_string(smart_contract.address_hash) == address_hash - assert smart_contract.compiler_version == valid_attrs["compiler"] + assert smart_contract.compiler_version == valid_attrs["compiler_version"] assert smart_contract.optimization == valid_attrs["optimization"] assert smart_contract.contract_source_code == valid_attrs["contract_source_code"] assert smart_contract.abi != nil @@ -52,7 +38,7 @@ defmodule Explorer.SmartContract.PublisherTest do invalid_attrs = %{ "contract_source_code" => "", - "compiler" => "", + "compiler_version" => "", "name" => "", "optimization" => "" } diff --git a/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs b/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs index a932786805..9bd7862f47 100644 --- a/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs +++ b/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs @@ -4,15 +4,56 @@ defmodule Explorer.SmartContract.Solidity.CodeCompilerTest do doctest Explorer.SmartContract.Solidity.CodeCompiler alias Explorer.SmartContract.Solidity.CodeCompiler + alias Explorer.Factory describe "run/2" do - test "compiles a smart contract using the solidity command line" do + setup do + {:ok, contract_code_info: Factory.contract_code_info()} + end + + test "compiles the latest solidity version", %{contract_code_info: contract_code_info} do + response = + CodeCompiler.run( + contract_code_info.name, + contract_code_info.version, + contract_code_info.source_code, + contract_code_info.optimized + ) + + assert {:ok, + %{ + "abi" => _, + "bytecode" => _, + "name" => _, + "opcodes" => _ + }} = response + end + + test "compiles a optimized smart contract", %{contract_code_info: contract_code_info} do + optimize = true + + response = + CodeCompiler.run( + contract_code_info.name, + contract_code_info.version, + contract_code_info.source_code, + optimize + ) + + assert {:ok, + %{ + "abi" => _, + "bytecode" => _, + "name" => _, + "opcodes" => _ + }} = response + end + + test "compile in an older solidity version" do + optimize = false name = "SimpleStorage" - optimization = false code = """ - pragma solidity ^0.4.24; - contract SimpleStorage { uint storedData; @@ -26,48 +67,74 @@ defmodule Explorer.SmartContract.Solidity.CodeCompilerTest do } """ - response = CodeCompiler.run(name, code, optimization) - - assert %{ - "contracts" => %{ - ^name => %{ - ^name => %{ - "abi" => _, - "evm" => %{ - "bytecode" => %{"object" => _} - } - } - } - } - } = response + version = "v0.1.3-nightly.2015.9.25+commit.4457170" + + response = CodeCompiler.run(name, version, code, optimize) + + assert {:ok, + %{ + "abi" => _, + "bytecode" => _, + "name" => _, + "opcodes" => _ + }} = response + end + + test "returns a list of errors the compilation isn't possible", %{ + contract_code_info: contract_code_info + } do + wrong_code = "pragma solidity ^0.4.24; cont SimpleStorage { " + + response = + CodeCompiler.run( + contract_code_info.name, + contract_code_info.version, + wrong_code, + contract_code_info.optimized + ) + + assert {:error, errors} = response + assert is_list(errors) end end - describe "generate_settings/2" do - test "creates a json file with the solidity compiler expected settings" do - name = "SimpleStorage" - optimization = false + describe "get_contract_info/1" do + test "return name error when the Contract name doesn't match" do + name = "Name" + different_name = "diff_name" - code = """ - pragma solidity ^0.4.24; + response = CodeCompiler.get_contract_info(%{name => %{}}, different_name) - contract SimpleStorage { - uint storedData; + assert {:error, :name} == response + end - function set(uint x) public { - storedData = x; - } + test "returns an empty list of errors for empty info" do + name = "Name" - function get() public constant returns (uint) { - return storedData; - } - } - """ + response = CodeCompiler.get_contract_info(%{}, name) + + assert %{"errors" => []} == response + end + + test "the contract info is returned when the name matches" do + contract_inner_info = %{"abi" => %{}, "bytecode" => "", "opcodes" => ""} + name = "Name" + contract_info = %{name => contract_inner_info} + + response = CodeCompiler.get_contract_info(contract_info, name) + + assert contract_inner_info == response + end + + test "the contract info is returned when the name matches with a `:` sufix" do + name = "Name" + name_with_sufix = ":Name" + contract_inner_info = %{"abi" => %{}, "bytecode" => "", "opcodes" => ""} + contract_info = %{name_with_sufix => contract_inner_info} - generated = CodeCompiler.generate_settings(name, code, optimization) + response = CodeCompiler.get_contract_info(contract_info, name) - assert String.contains?(generated, "contract SimpleStorage") == true - assert String.contains?(generated, "settings") == true + assert contract_inner_info == response end end end diff --git a/apps/explorer/test/explorer/smart_contract/solidity/compiler_version_test.exs b/apps/explorer/test/explorer/smart_contract/solidity/compiler_version_test.exs index 5e28f7aa6e..6e36da7255 100644 --- a/apps/explorer/test/explorer/smart_contract/solidity/compiler_version_test.exs +++ b/apps/explorer/test/explorer/smart_contract/solidity/compiler_version_test.exs @@ -24,7 +24,19 @@ defmodule Explorer.SmartContract.Solidity.CompilerVersionTest do end) assert {:ok, versions} = CompilerVersion.fetch_versions() - assert Enum.any?(versions, fn item -> item == {"0.4.9", "0.4.9"} end) == true + assert Enum.any?(versions, fn item -> item == "v0.4.9+commit.364da425" end) == true + end + + test "always returns 'latest' in the first item", %{bypass: bypass} do + Bypass.expect(bypass, fn conn -> + assert "GET" == conn.method + assert "/bin/list.json" == conn.request_path + + Conn.resp(conn, 200, solc_bin_versions()) + end) + + assert {:ok, versions} = CompilerVersion.fetch_versions() + assert List.first(versions) == "latest" end test "returns error when list of versions is not available", %{bypass: bypass} do diff --git a/apps/explorer/test/explorer/smart_contract/verifier_test.exs b/apps/explorer/test/explorer/smart_contract/verifier_test.exs index 1c7066d7fc..bb5d2ba5c5 100644 --- a/apps/explorer/test/explorer/smart_contract/verifier_test.exs +++ b/apps/explorer/test/explorer/smart_contract/verifier_test.exs @@ -5,84 +5,57 @@ defmodule Explorer.SmartContract.VerifierTest do doctest Explorer.SmartContract.Verifier alias Explorer.SmartContract.Verifier + alias Explorer.Factory describe "evaluate_authenticity/2" do - test "verifies the generated bytecode against bytecode retrieved from the blockchain" do - address_hash = "0x0f95fa9bc0383e699325f2658d04e8d96d87b90c" - - smart_contract_bytecode = - "0x608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a7230582040d82a7379b1ee1632ad4d8a239954fd940277b25628ead95259a85c5eddb2120029" - - created_contract_address = insert(:address, hash: address_hash, contract_code: smart_contract_bytecode) - - transaction = - :transaction - |> insert() - |> with_block() + setup do + {:ok, contract_code_info: Factory.contract_code_info()} + end - insert( - :internal_transaction_create, - transaction: transaction, - index: 0, - created_contract_address: created_contract_address, - created_contract_code: smart_contract_bytecode - ) + test "verifies the generated bytecode against bytecode retrieved from the blockchain", %{ + contract_code_info: contract_code_info + } do + contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) params = %{ - "contract_source_code" => - "pragma solidity ^0.4.24; contract SimpleStorage { uint storedData; function set(uint x) public { storedData = x; } function get() public constant returns (uint) { return storedData; } }", - "compiler" => "0.4.24", - "name" => "SimpleStorage", - "optimization" => false + "contract_source_code" => contract_code_info.source_code, + "compiler_version" => contract_code_info.version, + "name" => contract_code_info.name, + "optimization" => contract_code_info.optimized } - assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(address_hash, params) + assert {:ok, %{abi: abi}} = Verifier.evaluate_authenticity(contract_address.hash, params) assert abi != nil end - test "returns error when bytecoed doesn't match" do - address_hash = "0x0f95fa9bc0383e699325f2658d04e8d96d87b90c" + test "returns error when bytecode doesn't match", %{contract_code_info: contract_code_info} do + contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) - wrong_smart_contract_bytecode = - "0x6080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a723058207722b6ddfe522b31e50b878ced2f22d051e8e2cd19be7b4fba9686602b90ba2b0029" - - created_contract_address = insert(:address, hash: address_hash, contract_code: wrong_smart_contract_bytecode) - - transaction = - :transaction - |> insert() - |> with_block() - - insert( - :internal_transaction_create, - transaction: transaction, - index: 0, - created_contract_address: created_contract_address, - created_contract_code: wrong_smart_contract_bytecode - ) + different_code = "pragma solidity ^0.4.24; contract SimpleStorage {}" params = %{ - "contract_source_code" => - "pragma solidity ^0.4.24; contract SimpleStorage { uint storedData; function set(uint x) public { storedData = x; } function get() public constant returns (uint) { return storedData; } }", - "compiler" => "0.4.24", - "name" => "SimpleStorage", - "optimization" => false + "contract_source_code" => different_code, + "compiler_version" => contract_code_info.version, + "name" => contract_code_info.name, + "optimization" => contract_code_info.optimized } - assert {:error, :generated_bytecode} = Verifier.evaluate_authenticity(address_hash, params) + response = Verifier.evaluate_authenticity(contract_address.hash, params) + + assert {:error, :generated_bytecode} = response end - test "returns error when there is a compilation problem" do - address_hash = "0x0f95fa9bc0383e699325f2658d04e8d96d87b90c" + test "returns error when there is a compilation problem", %{contract_code_info: contract_code_info} do + contract_address = insert(:contract_address, contract_code: contract_code_info.bytecode) params = %{ "contract_source_code" => "pragma solidity ^0.4.24; contract SimpleStorage { ", - "compiler" => "0.4.24", - "name" => "SimpleStorage", - "optimization" => false + "compiler_version" => contract_code_info.version, + "name" => contract_code_info.name, + "optimization" => contract_code_info.optimized } - assert {:error, :compilation} = Verifier.evaluate_authenticity(address_hash, params) + assert {:error, :compilation} = Verifier.evaluate_authenticity(contract_address.hash, params) end end diff --git a/apps/explorer/test/support/factory.ex b/apps/explorer/test/support/factory.ex index 0972f87db1..a5b20cb046 100644 --- a/apps/explorer/test/support/factory.ex +++ b/apps/explorer/test/support/factory.ex @@ -29,6 +29,38 @@ defmodule Explorer.Factory do } end + def contract_address_factory do + %Address{ + hash: address_hash(), + contract_code: Map.fetch!(contract_code_info(), :bytecode) + } + end + + def contract_code_info do + %{ + bytecode: + "0x6080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a72305820f65a3adc1cfb055013d1dc37d0fe98676e2a5963677fa7541a10386d163446680029", + name: "SimpleStorage", + source_code: """ + pragma solidity ^0.4.24; + + contract SimpleStorage { + uint storedData; + + function set(uint x) public { + storedData = x; + } + + function get() public constant returns (uint) { + return storedData; + } + } + """, + version: "v0.4.24+commit.e67f0147", + optimized: false + } + end + def address_hash do {:ok, address_hash} = "address_hash" diff --git a/apps/explorer/test/support/fixture/smart_contract/solc_bin.json b/apps/explorer/test/support/fixture/smart_contract/solc_bin.json index 61c069d8dd..454cef29f7 100644 --- a/apps/explorer/test/support/fixture/smart_contract/solc_bin.json +++ b/apps/explorer/test/support/fixture/smart_contract/solc_bin.json @@ -5689,6 +5689,302 @@ "urls": [ "bzzr://965ce40ed5e4037a3b3916873b113052129082efec45f9aba767658515264bc5" ] + }, + { + "path": "soljson-v0.4.24-nightly.2018.5.14+commit.7a669b39.js", + "version": "0.4.24", + "prerelease": "nightly.2018.5.14", + "build": "commit.7a669b39", + "longVersion": "0.4.24-nightly.2018.5.14+commit.7a669b39", + "keccak256": "0xd6877c4e6919906f0c2ec5eebc8b54bd8abecf36619f1b9de53118d66890c4c2", + "urls": [ + "bzzr://83393f31311c2cd49f4c8564a9fbc6962be548a73c79a5967eb7423ee5cf59d7" + ] + }, + { + "path": "soljson-v0.4.24-nightly.2018.5.15+commit.b8b46099.js", + "version": "0.4.24", + "prerelease": "nightly.2018.5.15", + "build": "commit.b8b46099", + "longVersion": "0.4.24-nightly.2018.5.15+commit.b8b46099", + "keccak256": "0xeea56c1218e694e86024a3e18b04f5f428e182e39cfa94d0f4e21e5b2b3675cc", + "urls": [ + "bzzr://dbaaa698d20207fb480f5571ee110d2f5f9fc4825ec5fb7c1d0a66c235541d1e" + ] + }, + { + "path": "soljson-v0.4.24-nightly.2018.5.16+commit.7f965c86.js", + "version": "0.4.24", + "prerelease": "nightly.2018.5.16", + "build": "commit.7f965c86", + "longVersion": "0.4.24-nightly.2018.5.16+commit.7f965c86", + "keccak256": "0x7e34ded082f4403f672626691c86bac90cf3127047bdadd92218fcad9c5cc05e", + "urls": [ + "bzzr://cfa47b4d8d75df10651343eb3ef3609d6b1176a35a0c97faee45abeb7b033cf3" + ] + }, + { + "path": "soljson-v0.4.24+commit.e67f0147.js", + "version": "0.4.24", + "build": "commit.e67f0147", + "longVersion": "0.4.24+commit.e67f0147", + "keccak256": "0xee322e8f3117fcd7c196e88407d938846c096a3c62a51debd8a646f3aa228fcb", + "urls": [ + "bzzr://bbcf75b3549aaa4b68bdd805e5c5b8a0b0be6a964e068b7ef36c48431f44e8e1" + ] + }, + { + "path": "soljson-v0.4.25-nightly.2018.5.16+commit.3897c367.js", + "version": "0.4.25", + "prerelease": "nightly.2018.5.16", + "build": "commit.3897c367", + "longVersion": "0.4.25-nightly.2018.5.16+commit.3897c367", + "keccak256": "0x542d4ee7433cc87b0af481c0576c0eaa1e039564920cabaf05c470129028adab", + "urls": [ + "bzzr://59a32bef8105739bcfd4c510bfd79af4c8ed8c6a7044cc35226dcf728730c66e" + ] + }, + { + "path": "soljson-v0.4.25-nightly.2018.5.17+commit.4aa2f036.js", + "version": "0.4.25", + "prerelease": "nightly.2018.5.17", + "build": "commit.4aa2f036", + "longVersion": "0.4.25-nightly.2018.5.17+commit.4aa2f036", + "keccak256": "0xdb0c3ad580eece0e4be735bc393511d5c6da3a43e4d9bdae4bb301759db9a48b", + "urls": [ + "bzzr://d7518a74bdf87800dab785598f1194800cf74729b31bb2f2cf56884285bd3d7b" + ] + }, + { + "path": "soljson-v0.4.25-nightly.2018.5.18+commit.4d7b092c.js", + "version": "0.4.25", + "prerelease": "nightly.2018.5.18", + "build": "commit.4d7b092c", + "longVersion": "0.4.25-nightly.2018.5.18+commit.4d7b092c", + "keccak256": "0xdf46b3a863c003d7e5e4e5629dc95c120c34c2fc2379dfe686a91b0b513a0cf6", + "urls": [ + "bzzr://a40273ce36779aa9fa27bcda851a6bf9c7919830ebb27fd73e10e539a502a4dc" + ] + }, + { + "path": "soljson-v0.4.25-nightly.2018.5.21+commit.e97f9b6b.js", + "version": "0.4.25", + "prerelease": "nightly.2018.5.21", + "build": "commit.e97f9b6b", + "longVersion": "0.4.25-nightly.2018.5.21+commit.e97f9b6b", + "keccak256": "0xb5b57bc72c77a76e277e7eac82d041dfaa251288f727c13d1b22bbfbda039e32", + "urls": [ + "bzzr://058835dd41e4819be08eede51f46b0c75311f95b1e1079891d6d888d94074da6" + ] + }, + { + "path": "soljson-v0.4.25-nightly.2018.5.22+commit.849b1bd5.js", + "version": "0.4.25", + "prerelease": "nightly.2018.5.22", + "build": "commit.849b1bd5", + "longVersion": "0.4.25-nightly.2018.5.22+commit.849b1bd5", + "keccak256": "0x2770bcff9311f4cc1b11b31e8e186ce205a7376353979c9a3b122a00f11ba06d", + "urls": [ + "bzzr://9a7d1a775538ae6f416cef602b01edb0af53c3f740c2a321b8a7077af9c0f55e" + ] + }, + { + "path": "soljson-v0.4.25-nightly.2018.5.23+commit.18c651b7.js", + "version": "0.4.25", + "prerelease": "nightly.2018.5.23", + "build": "commit.18c651b7", + "longVersion": "0.4.25-nightly.2018.5.23+commit.18c651b7", + "keccak256": "0x8b21b478706c069b19746b7d088396ad1a7323acca051f7e3c9065ad172fff19", + "urls": [ + "bzzr://254ef850416cf8862bf2a9495f202099f56e6d39fc03fd177d2aefe5a0d9643c" + ] + }, + { + "path": "soljson-v0.4.25-nightly.2018.5.28+commit.c223b03.js", + "version": "0.4.25", + "prerelease": "nightly.2018.5.28", + "build": "commit.c223b03", + "longVersion": "0.4.25-nightly.2018.5.28+commit.c223b03", + "keccak256": "0x7b2fd7abeb5ee053f462ecb6e075b5435c5903ffc337956af5d5174d787c958e", + "urls": [ + "bzzr://001ab758e76c6a7368d943543d382557210c5edf37ffbdcaaa627f60319f8bc5" + ] + }, + { + "path": "soljson-v0.4.25-nightly.2018.5.30+commit.3f3d6df2.js", + "version": "0.4.25", + "prerelease": "nightly.2018.5.30", + "build": "commit.3f3d6df2", + "longVersion": "0.4.25-nightly.2018.5.30+commit.3f3d6df2", + "keccak256": "0xb8717d5ddd7cb2f8516b7e7bf0cae3802cd89659d3c2b408f5c9f4ce67545e9f", + "urls": [ + "bzzr://966adc290ef6f10203304c0866ad7c4bd233a93c7e74639b53647263fd02c71b" + ] + }, + { + "path": "soljson-v0.4.25-nightly.2018.6.3+commit.ef8fb63b.js", + "version": "0.4.25", + "prerelease": "nightly.2018.6.3", + "build": "commit.ef8fb63b", + "longVersion": "0.4.25-nightly.2018.6.3+commit.ef8fb63b", + "keccak256": "0x6e5eb8454f1e6f6248f609d839e2d459842326db86536f138341555b2985b58d", + "urls": [ + "bzzr://31d1b91ba0efa44ea689a2df2637de69fc09dc1a998a81df9fd1aad7514b9e65" + ] + }, + { + "path": "soljson-v0.4.25-nightly.2018.6.4+commit.a074d84.js", + "version": "0.4.25", + "prerelease": "nightly.2018.6.4", + "build": "commit.a074d84", + "longVersion": "0.4.25-nightly.2018.6.4+commit.a074d84", + "keccak256": "0x257b4782376e9f044df5126869745811fa2a00bce2d9fca3e0665d81244dead4", + "urls": [ + "bzzr://9a46af1a168690a684139ca2d2d4550932f3c11ab7a3d9493491be45319e0e9b" + ] + }, + { + "path": "soljson-v0.4.25-nightly.2018.6.5+commit.7422cd73.js", + "version": "0.4.25", + "prerelease": "nightly.2018.6.5", + "build": "commit.7422cd73", + "longVersion": "0.4.25-nightly.2018.6.5+commit.7422cd73", + "keccak256": "0x36271bbc780a45b1b5ed82f6e8e09a02caf78557a9548962e6162032f1a4014a", + "urls": [ + "bzzr://dc36676cf21c961dfd52651b82c85219f1843f578489195dcd6b6f48947d60b9" + ] + }, + { + "path": "soljson-v0.4.25-nightly.2018.6.6+commit.59b35fa5.js", + "version": "0.4.25", + "prerelease": "nightly.2018.6.6", + "build": "commit.59b35fa5", + "longVersion": "0.4.25-nightly.2018.6.6+commit.59b35fa5", + "keccak256": "0xcaa82fac0cbcf2e35782e45745cf830f97eee0bf27bba85d24de967d3e863e71", + "urls": [ + "bzzr://f631082277241bdbe1fcd92783f4864d60c7dd16bb96ff78a84e73cfbd7acce8" + ] + }, + { + "path": "soljson-v0.4.25-nightly.2018.6.7+commit.ddd256a6.js", + "version": "0.4.25", + "prerelease": "nightly.2018.6.7", + "build": "commit.ddd256a6", + "longVersion": "0.4.25-nightly.2018.6.7+commit.ddd256a6", + "keccak256": "0x164b5cbbab21103155cce12b2e6e819d7f6e748cdcf2ece5194ab4e9e2129ca3", + "urls": [ + "bzzr://3f305063ff8f7c7fb04fb7071120b3adae90a741e7ccbacc07d3535724f973f5" + ] + }, + { + "path": "soljson-v0.4.25-nightly.2018.6.8+commit.81c5a6e4.js", + "version": "0.4.25", + "prerelease": "nightly.2018.6.8", + "build": "commit.81c5a6e4", + "longVersion": "0.4.25-nightly.2018.6.8+commit.81c5a6e4", + "keccak256": "0x6d319de59f80795dc65ff9c71cbc88d5433664c2e208f6bc1ca3bcca011dd69e", + "urls": [ + "bzzr://ba5f458fc3a05465d52fe2e4502a5b2517cdde3ddaa1da3c6229494d2ee3f841" + ] + }, + { + "path": "soljson-v0.4.25-nightly.2018.6.11+commit.d0355619.js", + "version": "0.4.25", + "prerelease": "nightly.2018.6.11", + "build": "commit.d0355619", + "longVersion": "0.4.25-nightly.2018.6.11+commit.d0355619", + "keccak256": "0xc61567f505486fd9a7907b05e10ca297fe5d0f7f021c070449ac541795d781b3", + "urls": [ + "bzzr://990aad9205b4293bf6923f7d6abac9416928dccde529b7df52c379e1432e3dcc" + ] + }, + { + "path": "soljson-v0.4.25-nightly.2018.6.12+commit.56a965ea.js", + "version": "0.4.25", + "prerelease": "nightly.2018.6.12", + "build": "commit.56a965ea", + "longVersion": "0.4.25-nightly.2018.6.12+commit.56a965ea", + "keccak256": "0xdab0a9f25c5f5fb9f7a824eb01225ebf0f73d744b33049587b642179485fe059", + "urls": [ + "bzzr://5596d6d153d32f43557d738d9de4343e880fed7030d0b1c93792fa57c69dc8e5" + ] + }, + { + "path": "soljson-v0.4.25-nightly.2018.6.13+commit.3055e4ca.js", + "version": "0.4.25", + "prerelease": "nightly.2018.6.13", + "build": "commit.3055e4ca", + "longVersion": "0.4.25-nightly.2018.6.13+commit.3055e4ca", + "keccak256": "0x5332a1fa2f9e24d39a5013b117552c2adf7e130d6d5ea96d86db8f99b2126cbb", + "urls": [ + "bzzr://0b5c9b9ab9d15edf63fd9a14ec32d684c8a980f49f80d274d96edf0112ba30b9" + ] + }, + { + "path": "soljson-v0.4.25-nightly.2018.6.14+commit.baeabe1c.js", + "version": "0.4.25", + "prerelease": "nightly.2018.6.14", + "build": "commit.baeabe1c", + "longVersion": "0.4.25-nightly.2018.6.14+commit.baeabe1c", + "keccak256": "0x3a1ea681afc4be4ec049258d7c6d262e251e46bbb571b13e78b3ed11875dc040", + "urls": [ + "bzzr://9b80ae9f183780cea4f6e11c47a6dd2d845399da0537518fc834cbd8abba1d3a" + ] + }, + { + "path": "soljson-v0.4.25-nightly.2018.6.17+commit.1692f78b.js", + "version": "0.4.25", + "prerelease": "nightly.2018.6.17", + "build": "commit.1692f78b", + "longVersion": "0.4.25-nightly.2018.6.17+commit.1692f78b", + "keccak256": "0xf4f2d9990c605f24f4f8217f6c1249150b0709e76ed0f785af4a163f96d231ab", + "urls": [ + "bzzr://a73ad38f658292182d399010fbfbb6ac1069fa4b2e59b192bb1e04987aa074ca" + ] + }, + { + "path": "soljson-v0.4.25-nightly.2018.6.18+commit.4247b004.js", + "version": "0.4.25", + "prerelease": "nightly.2018.6.18", + "build": "commit.4247b004", + "longVersion": "0.4.25-nightly.2018.6.18+commit.4247b004", + "keccak256": "0xea2f1c0ab2bb9b8fe910d611f3a49e5fd37a3eb6d9adf9f5f8f58f9b488cffa5", + "urls": [ + "bzzr://fb554bd8f06cdb7d84e68a9900dca8c0a1cfcbd9a1a60912f95f8b6005539f51" + ] + }, + { + "path": "soljson-v0.4.25-nightly.2018.6.19+commit.c72e04c3.js", + "version": "0.4.25", + "prerelease": "nightly.2018.6.19", + "build": "commit.c72e04c3", + "longVersion": "0.4.25-nightly.2018.6.19+commit.c72e04c3", + "keccak256": "0x2a0509619ae9d8e48c995729031dcebcf9eec3f331db383f183e311e548e95c3", + "urls": [ + "bzzr://086d626be661d624f875e9a06c8308ca29cd0a1e5e96c098518fd6f458266a04" + ] + }, + { + "path": "soljson-v0.4.25-nightly.2018.6.20+commit.ba7fbf11.js", + "version": "0.4.25", + "prerelease": "nightly.2018.6.20", + "build": "commit.ba7fbf11", + "longVersion": "0.4.25-nightly.2018.6.20+commit.ba7fbf11", + "keccak256": "0x4d54a840997e7357d832ef48fcf254aa73d3cbd98ba2a2db3f4a334343382da7", + "urls": [ + "bzzr://1fe442eebadd93850fb579baa2772038a8c9b16c86cbfa41337cccacb1f58f9f" + ] + }, + { + "path": "soljson-v0.4.25-nightly.2018.6.21+commit.d104718.js", + "version": "0.4.25", + "prerelease": "nightly.2018.6.21", + "build": "commit.d104718", + "longVersion": "0.4.25-nightly.2018.6.21+commit.d104718", + "keccak256": "0x02d92eda9e226921dbc2daa5614718af522b398365698daf38acdac20c49f81a", + "urls": [ + "bzzr://0005e5682dd04228df8307d747263acf0c441fcadccceeb48c8174e92783aed2" + ] } ], "releases": { diff --git a/apps/explorer_web/lib/explorer_web/templates/address_contract_verification/new.html.eex b/apps/explorer_web/lib/explorer_web/templates/address_contract_verification/new.html.eex index 47c9a116c8..66af17d909 100644 --- a/apps/explorer_web/lib/explorer_web/templates/address_contract_verification/new.html.eex +++ b/apps/explorer_web/lib/explorer_web/templates/address_contract_verification/new.html.eex @@ -31,9 +31,9 @@