diff --git a/src/dapp/README.md b/src/dapp/README.md index fa7ac10cd..f84e3ceb0 100644 --- a/src/dapp/README.md +++ b/src/dapp/README.md @@ -234,30 +234,31 @@ To deploy a contract, you can use `dapp create`: dapp create Dapptutorial [] [] ``` -The `--verify` flag verifies the contract on etherscan (requires `ETHERSCAN_API_KEY`). +The `--verify` and `--verify-multiple` flags verify the contract/s on etherscan (requires `ETHERSCAN_API_KEY`). ## Configuration The commands of `dapp` can be customized with environment variables or flags. These variables can be set at the prompt or in a `.dapprc` file. -| Variable | Default | Synopsis | -|----------------------------|----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------| -| `DAPP_SRC` | `src` | Project Solidity source directory | -| `DAPP_LIB` | `lib` | Directory for installed Dapp packages | -| `DAPP_OUT` | `out` | Directory for compilation artifacts | -| `DAPP_ROOT` | `.` | Root directory of compilation | -| `DAPP_SOLC_VERSION` | n/a | Solidity compiler version to use | -| `DAPP_SOLC` | n/a | solc binary to use | -| `DAPP_VERBOSE` | n/a | Produce more `dapp test` output | -| `DAPP_LIBRARIES` | automatically deployed | Library addresses to link to | -| `DAPP_SKIP_BUILD` | n/a | Avoid compiling this time | -| `DAPP_LINK_TEST_LIBRARIES` | `1` when testing; else `0` | Compile with libraries | -| `DAPP_VERIFY_CONTRACT` | `yes` | Attempt Etherscan verification | -| `DAPP_STANDARD_JSON` | $(dapp mk-standard-json) | [Solidity compilation options](https://docs.soliditylang.org/en/latest/using-the-compiler.html#compiler-input-and-output-json-description) | -| `DAPP_REMAPPINGS` | $(dapp remappings) | [Solidity remappings](https://docs.soliditylang.org/en/latest/using-the-compiler.html#path-remapping) | -| `DAPP_BUILD_OPTIMIZE` | no | Activate Solidity optimizer | -| `DAPP_BUILD_OPTIMIZE_RUNS` | 200 | Set the optimizer runs | +| Variable | Default | Synopsis | +|---------------------------------|----------------------------|---------------------------------------------------------------------------------------------------------------------------------------| +| `DAPP_SRC` | `src` | Project Solidity source directory | +| `DAPP_LIB` | `lib` | Directory for installed Dapp packages | +| `DAPP_OUT` | `out` | Directory for compilation artifacts | +| `DAPP_ROOT` | `.` | Root directory of compilation | +| `DAPP_SOLC_VERSION` | n/a | Solidity compiler version to use | +| `DAPP_SOLC` | n/a | solc binary to use | +| `DAPP_VERBOSE` | n/a | Produce more `dapp test` output | +| `DAPP_LIBRARIES` | automatically deployed | Library addresses to link to | +| `DAPP_SKIP_BUILD` | n/a | Avoid compiling this time | +| `DAPP_LINK_TEST_LIBRARIES` | `1` when testing; else `0` | Compile with libraries | +| `DAPP_VERIFY_CONTRACT` | `yes` | Attempt Etherscan verification | +| `DAPP_VERIFY_CONTRACT_MULTIPLE` | `yes` | Attempt Etherscan multifile verification | +| `DAPP_STANDARD_JSON` | $(dapp mk-standard-json) | [Solidity compilation options](https://docs.soliditylang.org/en/latest/using-the-compiler.html#compiler-input-and-output-json-description) | +| `DAPP_REMAPPINGS` | $(dapp remappings) | [Solidity remappings](https://docs.soliditylang.org/en/latest/using-the-compiler.html#path-remapping) | +| `DAPP_BUILD_OPTIMIZE` | no | Activate Solidity optimizer | +| `DAPP_BUILD_OPTIMIZE_RUNS` | 200 | Set the optimizer runs | A global (always loaded) config file is located in `~/.dapprc`. A local `.dapprc` can also be defined in your project's root, which overrides variables in the global config. @@ -421,7 +422,7 @@ for key bindings for navigation. dapp-create -- deploy a compiled contract (--verify on Etherscan) Usage: dapp create or dapp create : - Add --verify and export your ETHERSCAN_API_KEY to auto-verify on Etherscan + Add --verify or --verify-multiple and export your ETHERSCAN_API_KEY to auto-verify on Etherscan ### `dapp address` @@ -468,7 +469,7 @@ Requires `ETHERSCAN_API_KEY` to be set. `seth chain` will be used to determine on which network the contract is to be verified. -Automatically run when the `--verify` flag is passed to `dapp create`. +Automatically run when the `--verify` or `--verify-multiple` flag is passed to `dapp create`. ### `dapp mk-standard-json` @@ -481,3 +482,4 @@ The following environment variables can be used to override settings: - `DAPP_BUILD_OPTIMIZE` - `DAPP_BUILD_OPTIMIZE_RUNS` - `DAPP_LIBRARIES` +- `DAPP_VERIFY_CONTRACT_MULTIPLE` diff --git a/src/dapp/libexec/dapp/dapp b/src/dapp/libexec/dapp/dapp index 243c32ab8..372c72b3e 100755 --- a/src/dapp/libexec/dapp/dapp +++ b/src/dapp/libexec/dapp/dapp @@ -35,6 +35,7 @@ ### ### Deployment options: ### verify verify contract on etherscan +### verify-multiple multifile verification on etherscan ### ### Contract verifying options: ### async don't wait for confirmation @@ -81,6 +82,7 @@ smtdebug print the SMT queries produced by hevm Deployment options: verify verify contract on etherscan +verify-multiple multifile verification on etherscan Contract verifying options: async don't wait for confirmation @@ -161,6 +163,7 @@ while [[ $1 ]]; do export HEVM_RPC=yes;; --verify) export DAPP_VERIFY_CONTRACT=yes;; + --verify-multiple) export DAPP_VERIFY_CONTRACT_MULTIPLE=yes;; --async) export DAPP_ASYNC=yes;; diff --git a/src/dapp/libexec/dapp/dapp-create b/src/dapp/libexec/dapp/dapp-create index e27fe3672..644562ce7 100755 --- a/src/dapp/libexec/dapp/dapp-create +++ b/src/dapp/libexec/dapp/dapp-create @@ -47,4 +47,10 @@ address=$(set -x; seth send --create "$bin" "${type/constructor/${1#*:}}" "${@:2 dapp verify-contract "$path":"${1#*:}" "$address" "${@:2}" } +[[ $DAPP_VERIFY_CONTRACT_MULTIPLE ]] && { + echo >&2 "Verifying contracts at $address" + sleep 5 # give etherscan some time to process the block + dapp verify-contracts "$path":"${1#*:}" "$address" "${@:2}" +} + seth --to-checksum-address "$address" diff --git a/src/dapp/libexec/dapp/dapp-mk-standard-json b/src/dapp/libexec/dapp/dapp-mk-standard-json index fc15d5394..3dfc27211 100755 --- a/src/dapp/libexec/dapp/dapp-mk-standard-json +++ b/src/dapp/libexec/dapp/dapp-mk-standard-json @@ -27,9 +27,19 @@ for root, dirnames, filenames in os.walk(src): files.append(os.path.join(root, filename)) tmpljson["sources"]={} -for f in files: - tmpljson["sources"][f]={} - tmpljson["sources"][f]["urls"] = [f] + +if os.getenv('DAPP_VERIFY_CONTRACT_MULTIPLE') == "yes": + for f in files: + if f.endswith(".t.sol"): + continue + else: + tmpljson["sources"][f]={} + content = open(f, "r"); + tmpljson["sources"][f]["content"] = content.read() +else: + for f in files: + tmpljson["sources"][f]={} + tmpljson["sources"][f]["urls"] = [f] if os.getenv('DAPP_REMAPPINGS') is None or os.getenv('DAPP_REMAPPINGS') == "": tmpljson["settings"].pop("remappings", None) diff --git a/src/dapp/libexec/dapp/dapp-verify-contract b/src/dapp/libexec/dapp/dapp-verify-contract index edd3f7df5..ad2013849 100755 --- a/src/dapp/libexec/dapp/dapp-verify-contract +++ b/src/dapp/libexec/dapp/dapp-verify-contract @@ -1,5 +1,5 @@ #!/usr/bin/env bash -### dapp-verify-contract -- verify contract souce on etherscan +### dapp-verify-contract -- verify contract source on etherscan ### Usage: dapp verify-contract :
[constructorArgs] set -e @@ -41,7 +41,7 @@ address=${2?contractaddress} # combined-json has a sourceList field if [[ $(jq .sourceList "$DAPP_JSON") == null ]]; then contract=$(<"$DAPP_JSON" jq -r ".contracts[\"${path/:*/}\"][\"$name\"]") -else +else contract=$(<"$DAPP_JSON" jq -r ".contracts[\"$path\"]") fi meta=$(jshon <<<"$contract" -e metadata -u) diff --git a/src/dapp/libexec/dapp/dapp-verify-contracts b/src/dapp/libexec/dapp/dapp-verify-contracts new file mode 100755 index 000000000..fb38c4592 --- /dev/null +++ b/src/dapp/libexec/dapp/dapp-verify-contracts @@ -0,0 +1,150 @@ +#!/usr/bin/env bash +### dapp-verify-contracts -- verify contracts source on etherscan +### Usage: dapp verify-contracts :
[constructorArgs] + +set -e + +[[ $ETHERSCAN_API_KEY ]] || { + cat >&2 <<. + + You need an Etherscan Api Key to verify contracts. + Create one at https://etherscan.io/myapikey + + Then export it with \`export ETHERSCAN_API_KEY=xxxxxxxx' + +. + exit 1 +} + +[[ $1 ]] || (dapp verify-contracts --help >&2 && exit 1) +[[ $2 ]] || (dapp verify-contracts --help >&2 && exit 1) + +chain=$(seth chain) +case "$chain" in + ethlive|mainnet) + export ETHERSCAN_API_URL=https://api.etherscan.io/api + export ETHERSCAN_URL=https://etherscan.io/address + ;; + ropsten|kovan|rinkeby|goerli) + export ETHERSCAN_API_URL=https://api-$chain.etherscan.io/api + export ETHERSCAN_URL=https://$chain.etherscan.io/address + ;; + *) + echo >&2 "Verification only works on mainnet, ropsten, kovan, rinkeby, and goerli." + exit 1 +esac + +path=${1?contractname} +name=${path#*:} +contractName=$(basename "$path") +address=${2?contractaddress} + +# combined-json has a sourceList field +if [[ $(jq .sourceList "$DAPP_JSON") == null ]]; then + contract=$(<"$DAPP_JSON" jq -r ".contracts[\"${path/:*/}\"][\"$name\"]") +else + contract=$(<"$DAPP_JSON" jq -r ".contracts[\"$path\"]") +fi +meta=$(jshon <<<"$contract" -e metadata -u) +version=$(jshon <<<"$meta" -e compiler -e version -u) + +abi=$(jq '.["abi"]' -r <<< "$contract") +type=$(seth --abi-constructor <<< "$abi") +constructor=${type/constructor/${1#*:}} + +if [[ $3 ]]; then + constructorArguments=$(seth calldata "$constructor" "${@:3}") + constructorArguments=${constructorArguments#0x} + constructorArguments=${constructorArguments:8} +fi + + +# Etherscan requires leading 'v' which isn't in the artifacts +version="v${version}" + +# Get the list of supported solc versions and compare +# Etherscan uses the js solc, which is not guaranteed to match the C distribution signature + +version_list=$(curl -fsS "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/bin/list.txt") +# There have been a couple of instances where the solc js release used by +# Etherscan does not match the tag of the C distributions. +if [[ $version_list != *"$version"* ]]; then + regex="(.+commit+.)" + # Version incompatible with js release + echo "Compiler version $version is not compatible with etherscan" + if [[ $version =~ $regex ]]; then + version_proto=${BASH_REMATCH[1]} + version=$(echo "$version_list" | grep -o "${version_proto}\{8\}") + echo "Attempting ${version}" + fi +fi + +# Standard Input JSON + +inputJSON=$(dapp mk-standard-json) + +params=( +"module=contract" "action=verifysourcecode" +"contractname=$contractName" "contractaddress=$address" +"codeformat=solidity-standard-json-input" +"apikey=$ETHERSCAN_API_KEY" +) + +query=$(printf "&%s" "${params[@]}") + +count=0 +while [ $count -lt 5 ]; do + sleep 10 + + response=$(curl --location --request POST "$ETHERSCAN_API_URL" \ + --header 'Content-Type: application/x-www-form-urlencoded' \ + -d "$query" \ + --data-urlencode "compilerversion=$version" \ + --data-urlencode "sourceCode=$inputJSON" \ + --data-urlencode "constructorArguements=$constructorArguments") + # NOTE: the Arguements typo is in etherscan's API + + status=$(jshon <<<"$response" -e status -u) + guid=$(jshon <<<"$response" -e result -u) + message=$(jshon <<<"$response" -e message -u) + count=$((count + 1)) + + [[ $status = 1 ]] && break; +done + +[[ $status = 0 && $message = "Contract source code already verified" ]] && { + echo >&2 "Contract source code already verified." + echo >&2 "Go to $ETHERSCAN_URL/$2#code" + exit 0 +} + +[[ $status = 0 ]] && { + echo >&2 "There was an error verifying this contract." + echo >&2 "Response: $message" + echo >&2 "Details: $guid" + exit 1 +} + + + + +[[ $DAPP_ASYNC == yes ]] && exit + +sleep 20 +response=$(curl -fsS "$ETHERSCAN_API_URL" \ +-d "module=contract&action=checkverifystatus&guid=$guid&apikey=$ETHERSCAN_API_KEY") + +status=$(jshon <<<"$response" -e status -u) +result=$(jshon <<<"$response" -e result -u) + +[[ $status = 1 ]] && { + echo >&2 "$result" + echo >&2 "Go to $ETHERSCAN_URL/$2#code" + exit 0 +} + +[[ $status = 0 ]] && { + echo >&2 "Failure" + echo >&2 "$result" + exit 1 +}