Source code verification is crucial when deploying a smart contract on Ethereum that you expect others to interact with:
- It allows everyone to verify at a glance that the deployed contract is using the source code you advertise instead of having to download and compile the repository (and this might not even be possible for some due to metadata being added to the bytecode).
- It makes it possible to easily interact with the contract functions on Etherscan without the need for a GUI.
- Tools like Brownie can automatically fetch verified contracts for use in testing and scripts.
- Proxy patterns can be automatically detected by Etherscan and other tools.
There is really no reason to not verify your contracts, except if you are trying to hide something. Emphasis on trying, as a skilled user can still de-compile your contract to derive most of the functionality.
The real barrier to verifying contracts is how non-trivial it is. Etherscan offers an interface to do it, but you need to fill in a lot of information, including the complete source code used in the project. This is not easy to do manually when your code is split across several contracts, interfaces and external libraries.
Automatic Source Code Verification is Here!
Finally, as of version 1.13.0 Brownie supports simple, powerful, automated source code verification for Etherscan! Gone are the days of manually scraping together a flattened source file.
To verify a contract while deploying it, simply add the publish_source = True
argument to the deploy command:
>>> Token.deploy("My Token", "MTK", 18, 10e18, {'from': myAccount}, publish_source=True)Transaction sent: 0xc4ad5f3a9eb5ed84d6f9de5af0f79ff7abe05eaf3f2090145037e0f1ce5b55ab
Gas price: 1.0 gwei Gas limit: 567972 Nonce: 75
Token.constructor confirmed - Block: 9421160 Gas used: 516339 (90.91%)
Token deployed at: 0x7cACfD0D276143E4cD91225bA5f47F1dcDC3e07BWaiting for etherscan to process contract...
Verification submitted successfully. Waiting for result...
Verification complete. Result: Pass - Verified
You can see this example result on Etherscan here.
It is even possible to verify contracts that you deployed earlier, as long as you didn’t change any of the code. This is done with ContractContainer.publish_source(deployed_contract)
:
>>> token_contract = Token.at("0xEe4dc2a6A212500842D7bB34a24fE2d33Df8CFD1") # use the address where the contract is deployed
>>> Token.publish_source(token_contract)
Verification submitted successfully. Waiting for result...
Verification complete. Result: Pass - Verified
Common Pitfalls
There are a few things you need to keep in mind to successfully use this feature.
Supported Networks
Source verification only works for networks supported by Etherscan. Currently these are: Mainnet, Ropsten, Kovan, Rinkeby and Goerli. Attempting to publish the source on any other network, including local testnets, will fail.
Etherscan API Url
You need to set the Etherscan API url for the network you are using in the brownie network config file. By default, all these urls will be set correctly; if you have changed them, here is how it should look:
live:
- name: Ethereum
networks:
- name: Ropsten (Infura)
chainid: 3
id: ropsten
host: https://mainnet.infura.io/v3/$WEB3_INFURA_PROJECT_ID
explorer: https://api-ropsten.etherscan.io/api
You can check and verify your current settings by typing brownie networks list true
.
Etherscan API Token
To use the API source verification feature, you must provide an Etherscan API token. Luckily, these tokens are free and easy to obtain. Here is an instruction on how to get one. Once you have it, add it to the environment variables by typing export ETHERSCAN_TOKEN=YourToken
.
Infura API Token
If you decide to use Infura as your provider, you will also need an Infura Project Key. How to get one and set it up is described here in the Brownie docs. Of course you can just use your personal node or any other service as well!
Compiler Version
The feature works for all solidity compiler versions supported by Brownie. Vyper currently can’t work because Etherscan offers no API for verifying Vyper source code.
Additionally, you need to make sure EVERY contract in your project is compiled using the same compiler version, including all external libraries. This is again, because Etherscan only accepts a single compiler version. You can check this by deleting the build folder and running brownie compile
.
Troubleshooting
After you considered all of the common pitfalls and your verification still fails, you should first try to delete the build folder and re-compile the project. If that doesn’t help, you can view all the information used by brownie to verify the source code by calling YourContract.get_verification_info()
. Call this on the ContractContainer
, not the deployed contract. Then manually check each field and try to find the problem this way.
If after all this, the verification still fails, it is likely due to an edge case we didn’t consider when implementing the feature. Please open an issue on the brownie repository with a link to your Brownie project and we will try to fix it.
Technical Details and Futher Information
Brownie’s publish source functionality will attempt to combine the relevant source code from all used files in your project and put them into a single flattened .sol
file. The structure of the flattened file looks as follows:
- License Identifier: All source files are searched for a valid license identifier that is also supported by Etherscan. If multiple are found, the one used in the main file (this is where the contract you deploy is located) will be used.
- Pragma Version Statement: This is pulled from the compiler output and will specify the version used to compile the project.
- Further Pragma Statements: Currently only
pragma Experimental ABIEncoderV2;
exists. If any of the relevant files use a supported pragma statement, it will be added to the flattened file. - Global Enums and Structs: In Solidity enums and structs can be defined outside the scope of a contract, interface or library. These are global and accessible for every contract, interface and library. The flattener will search for these global variables in every relevant file, remove duplicates and add them to the flattened file after the pragma statements.
- Source Code: Finally, the relevant source code for each contract, interface and library is added to the flattened file. The order of these parts depends on the dependency structure and makes sure dependencies are always listed before the parts that use them. The main contract that you are deploying is always listed last.
Here is a sample of a flattened file:
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;// Global Enums and Structsenum State {Inactive, Active, Stopped}struct Info {
State state;
uint256 version;
}// Part: ShortSafeMathlibrary ShortSafeMath {
function add(uint a, uint b) internal pure returns (uint c) {
c = a + b;
require(c >= a);
return c;
}
}// File: MyContract.sol/**
@title Test Contract
*/
contract MyContract {
using ShortSafeMath for uint256;
Info info public = Info(State.Inactive);
function start() public {
info.state = State.Active;
}
}
Futher Reading on Brownie
If you enjoyed this, please follow the Brownie Twitter and like and share our content! Help get the word out about and show others what’s possible when developing and testing their contracts.
You can also follow Ben on Medium or follow me on Medium and check out other articles we have written, and join the Brownie Gitter to chat with and learn from other like-minded developers.