Tools for verifying Hedera smart contracts using standard open source libraries. The verification solution is based on Sourcify, the Smart Contract verification service part of Ethereum.
Install
Make sure both the server
submodule sourcify
and the repository
submodule h5ai-nginx
are present with
git submodule update --init --recursive
These repo submodules are sourcify
and h5ai-nginx
, which corresponds to the server
and repository
Sourcify upstream services respectively.
Instead of forking those repos, we attach them here directly in order to consume upstream updates more easily.
Given that the ui
changes are Hedera specific, we decided to keep our ui
fork with custom modifications.
Apply the Hedera customization patch to the sourcify
submodule (execute this only once)
npm run server:patch
To start the services server
, repository
and ui
run
docker compose up --detach
This command uses the
compose.yaml
located in the root folder of the repo. Go to Use Docker Images for more details.
Verify all the services are up and running.
This assumes the default ports (per .env
) are used.
- Open http://localhost:5555/chains. This should return a JSON value containing the 3 Hedera public networks, Hedera Mainnet, Hedera Testnet and Hedera Previewnet, in addition to a few well-known development networks.
- Open http://localhost:10000.
This should open the Repository
select-contract-form
. The options available for the Chain selection should be networks described above. - Open http://localhost:5555/api-docs.
This should open the Open API docs showcasing all endpoints offered by the
server
service. - Open http://localhost:5555/files/contracts/296.
This should return a JSON value containing the addresses of all contracts verified on testnet (or report error
"Contracts have not been found!"
if nothing has been verified yet) - Open http://localhost:3001. This should bring up the Verifier page.
To start the repository service, run
docker compose up --detach repository
To start the server
run
cd sourcify
npm ci
npm run build:lerna
cp ../test/sourcify-chains.json ./services/server/dist/
npm run server:start
To start and bring up the UI, run in a different terminal
cd ui
npm ci
npm run start
See README in ui
for more details.
To use the Mirror Node explorer with your verification instance, add this network entry to the array at /app/networks-config.json
{
"name": "localnet2",
"displayName": "LOCALNET2",
"url": "http://localhost:5551/",
"ledgerID": "02",
"sourcifySetup": {
"activate": true,
"repoURL": "http://repository.local/contracts/",
"serverURL": "http://localhost:5555/",
"verifierURL": "https://localhost/#/",
"chainID": 298
}
}
Tip
You may need to authenticate to the GitHub container registry ghcr.io
using a Personal Access Token as described here to pull the Docker images.
You can either use pre-built Docker images from the GitHub container repository or build the images locally.
Hedera verification service uses 3 images
server
[Verifier Server]. This service provides the actual verification of Smart Contracts. Its main task is to compile input Solidity sources and check compiler results. It checks compilers results against the bytecode retrieved from an Ethereum-compatible network, e.g., JSON-RPC Relay. Other services interact with it through its REST API. You can inspect the endpoints provided by visiting/api-docs
(OpenAPI generated docs) on theserver
, e.g., https://server-verify.hashscan.io/api-docs/. A successful verification stores the contracts sources under Repository Volume.repository
[Repository]. Provides a verified Smart Contract front end lookup and explorer. It reads verified smart contracts from the Repository Volume.ui
[Verifier UI]. A user frontend to verify and lookup Smart Contracts.
Note
Note that unlike Sourcify, we do not use the monitor
service given that we do not use IPFS verification.
That is, we only use server
and repository
services from upstream Sourcify.
Currently we host two UIs for contract verification, our custom UI https://verify.hashscan.io/ and the VERIFY CONTRACT
within https://hashscan.io/.
Both use the verification API https://server-verify.hashscan.io/api-docs/.
The
ui
image needs some front-end customizations, therefore we keep our own fork of the image.
C4Container
title Container Diagram for Smart Contract Verification System
Container_Boundary(scvs, "Smart Contract Verification System") {
Container(ui, "Verifier UI", "JavaScript, React", "Customized Sourcify Verifier front-end")
Container(server, "Verifier Server", "JavaScript, Node", "Provides the REST API that enables verification of Smart Contracts, includes a Solidity compiler")
Container(repo, "Repository", "nginx", "Provides a front-end to lookup and view verified Smart Contracts")
ContainerDb_Ext(repo_vol, "Repository Volume", "File System Volume", "Stores verified smart contracts")
}
System_Ext(eth, "Ethereum-compatible Network (json-rpc-relay)", "Provides the source of truth to fetch Smart Contract bytecode")
Rel(server, eth, "Uses", "JSON-RPC `eth_getCode`")
UpdateRelStyle(server, eth, $offsetY="-50", $offsetX="-140")
Rel(ui, server, "Uses", "REST")
Rel(server, repo_vol, "Mounts, read&write", "/tmp/sourcify/repository")
Rel_Back(repo, repo_vol, "Mounts, read", "/data")
See compose.yaml
for more details on how to setup and customize each service.
Pull all needed images
docker compose pull
or each one individually
docker pull ghcr.io/hashgraph/hedera-sourcify/ui:main
docker pull ghcr.io/hashgraph/hedera-sourcify/server:main
docker pull ghcr.io/hashgraph/hedera-sourcify/repository:main
Run the following to build the server
, repository
and ui
images locally
docker compose build
docker compose up --detach
Open http://localhost:3001 to bring up the UI Verifier page.
You can visit http://localhost:5555/api-docs/ to see the OpenAPI docs.
Verify your chain configuration is visible from http://localhost:5555/chains.
The repository path is /tmp/sourcify/repository
Run
docker compose down
Important
The reset script for Docker works by deleting the repository of verified contracts on the specified network (testnet
and previewnet
) through the server
service.
In order for it to work properly, the contracts repository must be mounted on /data
in the server
service.
To reset testnet
docker exec server-main /home/app/hedera-reset-docker.sh testnet
To reset previewnet
docker exec server-main /home/app/hedera-reset-docker.sh previewnet
The following tables describe the configuration items used by the different services
The ui service is a single page application based on React.
As such, it cannot be configured by environment variables at runtime.
It reads its configuration from a file located at the following path /usr/share/nginx/html/config.json
.
In deployment, the actual configuration can be provided to the container via a mount point.
Example contents for config.json
{
"SERVER_URL": "https://server.sourcify-integration.hedera-devops.com",
"REPOSITORY_SERVER_URL": "https://repository.sourcify-integration.hedera-devops.com",
"EXPLORER_URL": "http://localhost:8080",
"BRAND_PRODUCT_LOGO_URL": "http://example.com/path/to/my-logo.jpg",
"TERMS_OF_SERVICE_URL": "http://example.com/path/to/my-terms.html",
"REMOTE_IMPORT": false,
"GITHUB_IMPORT": false,
"CONTRACT_IMPORT": false,
"JSON_IMPORT": false,
"OPEN_IN_REMIX": false,
"CREATE2_VERIFICATION": false
}
The following properties can be provided in config.json
Name | Description |
---|---|
SERVER_URL |
URL of the server (from outside the cluster). |
REPOSITORY_SERVER_URL |
HTTP port exposed by container |
EXPLORER_URL |
URL of the mirror-node explorer |
BRAND_PRODUCT_LOGO_URL |
URL of the header top left product logo (default is Hedera logo) |
TERMS_OF_SERVICE_URL |
URL of the terms-of-service document linked from bottom of page (default is no link) |
REMOTE_IMPORT |
Flag to activate mode "Import from remote" (default is false) |
GITHUB_IMPORT |
Flag to activate mode "Import from GitHub" (default is false) |
CONTRACT_IMPORT |
Flag to activate mode "Import from contract's metadata" (default is false) |
JSON_IMPORT |
Flag to activate mode "Import contracts from Solidity's Standard JSON Input" (default is false) |
OPEN_IN_REMIX |
Flag to activate link "Open in Remix" (default is false) |
CREATE2_VERIFICATION |
Flag to activate create2 verification (default is false) |
The favicon may be customized by providing alternative versions of the 3 following files: manifest.json
, favicon.ico
, favicon-16x16.png
, favicon-32x32.png
and passing them to the ui
service via mount points.
This can be done for instance by adding the following to the definition of the ui
service in the compose.yaml
file used
volumes:
- type: bind
source: ./manifest.json
target: /usr/share/nginx/html/manifest.json
- type: bind
source: ./favicon.ico
target: /usr/share/nginx/html/favicon.ico
- type: bind
source: ./favicon-16x16.png
target: /usr/share/nginx/html/favicon-16x16.png
- type: bind
source: ./favicon-32x32.png
target: /usr/share/nginx/html/favicon-32x32.png`
The following settings are set in a local.js
file needed by the server at runtime
Name | Example value | Description |
---|---|---|
server.port |
5555 |
HTTP port used inside container |
repositoryV1.path |
/data | Path of the mount point of the verified contract repository (inside container) |
solcRepo |
/home/data/solc-bin/linux-amd64 | Path where Solidity compiler binaries will be saved (inside container) |
solJsonRepo |
/home/data/solc-bin/soljson | Path where Solidity JS compilers will be saved (inside container) |
corsAllowdOrigins |
[/^https?:\/\/(?:.+\.)?sourcify.dev$/] |
List of regexes that will be allowed by CORS inside the server |
Tip
See server's README
for more details.
This sourcify-chains.json
should be used to configure Hedera chains.
The host.docker.internal
hostname is used to make connections between containers.
See https://docs.docker.com/desktop/networking/#use-cases-and-workarounds for more details.
{
"295": {
"sourcifyName": "Hedera Mainnet",
"supported": true
},
"296": {
"sourcifyName": "Hedera Testnet",
"supported": true
},
"297": {
"sourcifyName": "Hedera Previewnet",
"supported": true
},
"298": {
"sourcifyName": "Hedera Localnet",
"supported": true,
"rpc": [
"http://host.docker.internal:7546"
]
}
}
You can customize OpenAPI Servers list by changing the servers.yaml
file.
For example
- description: The current REST API server
url: ""
- description: The production REST API server
url: "https://server-verify.hashscan.io"
- description: The staging REST API server
url: "https://server-sourcify.hedera-devops.com"
- description: Local development server address on default port 5002
url: "http://localhost:5002"
The repository service encompasses a single page application based on React and a web server.
In deployment, the actual configuration can be provided to the container via the same mount point as the one provided to the ui,
even though the only useful item for the repository is the following
"SERVER_URL": "https://server.sourcify-integration.hedera-devops.com"
value.
Given we leverage the Sourcify code base as is, we maintain only a subset of Sourcify server tests against a local chain and a basic non-regression server test using Hedera local node.
First compile the Sourcify server.
cd ./sourcify
and run
npm ci
npm run build:lerna
Then (from the repo root) run the Sourcify server tests with
npm run test:server
Important
Do not update the ganache
dependency.
Its latest version 7.9.2
(and most probably the last one given Ganache is no longer supported)
has some dependency issues that for some reason makes fsevents
non-optional.
This in turn makes non-darwin
installations, e.g., Github Actions jobs, to fail when running npm ci
with
npm ERR! notsup Unsupported platform for [email protected]: wanted {"os":"darwin"} (current: {"os":"linux"})
The Hedera local node variables HEDERA_NETWORK
, OPERATOR_ACCOUNT_ID
and OPERATOR_KEY
are defined in test/.env.test
.
Start Hedera local node with
npm run local-node:start
In another terminal session, cd ./sourcify
and start the Sourcify server with
npm ci
npm run build:lerna
cp ../test/sourcify-chains.json ./services/server/dist/
npm run server:start
Note
The sourcify-chains.json
is used to setup both the chain ID and JSON-RPC for the Hedera Localnet
network.
Finally (from the repo root) run the server tests
npm run test:hedera
See tools
folder to setup development environments to work with a custom verification deployment.
To release a new version X.Y.Z
follow the next steps.
First, create a release branch.
For example use the name release/X.Y
for a minor release.
Bump versions in a new PR against this release branch to the target version, X.Y.Z-rc1
and merge it back into the release branch, i.e., release/X.Y
.
Create another PR against main
for snapshot bumping, i.e.,X.(Y+1).0-SNAPSHOT
.
Only perform this step for rc
releases, for ga
releases there should be already a release/X.Y
branch.
The repo has Github Actions automation to generate Docker images based on the latest changes in a branch.
To trigger image generation in GitHub Actions for release version X.Y.Z
push a new tag using the following commands
git checkout release/X.Y
git pull
git tag vX.Y.Z
git push origin vX.Y.Z
When the workflow is done, the images should be published under https://github.com/orgs/hashgraph/packages?repo_name=hedera-sourcify. Verify that everything works as expected. You can use the following checklist to make sure the new release, either integration or production, was successful.
- Check available Hedera public networks. Make sure both the endpoint https://server-verify.hashscan.io/chains and Chain select list in https://repository-verify.hashscan.io/select-contract/ returns the Hedera public networks,
mainnet
,testnet
andpreviewnet
. - Check
servers
list. Make sure Servers listed in https://server-verify.hashscan.io/api-docs/ are properly set to Hedera and local servers. - Verify a contract using Hashscan. Deploy a contract with your favorite tool. Verify it using the
VERIFY CONTRACT
button in the Contract view in Hashscan. Make sure the verified contract is visible from therepository
. See How to Verify a Smart Contract on HashScan for more details. - Verify a contract using the Verifier UI. Deploy a contract with your favorite tool. Verify it using the Verifier UI. Make sure the verified contract is visible from the
repository
. Make sure the verified contract is visible as such in the Contract view in Hashscan. - Verify a contract using a Hardhat project. Make sure the
hardhat-verify
plugin is able to verify a contract in the deployed instance. See tools/README for more details. - Verify a contract using a Foundry project. Make sure the
forge create
andforge-contract
commands are able to verify a contract in the deployed instance. See tools/README for more details. - Ensure your deployed contracts are listed. Use the endpoint https://server-verify.hashscan.io/files/contracts/296 (change the domain if necessary) and make sure the deployed contracts are listed there.
Once you ensure the new release works properly, create a GitHub Release to let users and developers of what has changed. Go to https://github.com/hashgraph/hedera-sourcify/releases and follow the steps under Draft a new release.
If you have a question on how to use the product, please see our support guide.
Contributions are welcome. Please see the contributing guide to see how you can get involved.
This project is governed by the Contributor Covenant Code of Conduct. By participating, you are expected to uphold this code of conduct. Please report unacceptable behavior to [email protected].
Please do not file a public ticket mentioning the vulnerability. Refer to the security policy defined in the SECURITY.md.