Blockscout
SupportWebsiteGithubDiscord
  • Blockscout Open-Source Explorer
  • 💡About BlockScout
    • Features
      • Ethereum Bytecode Database Microservice
      • Blockscout Redesign
    • Chains Using Blockscout
    • Partners & Integrations
      • RaaS Providers
      • Vera: Verifier Alliance
        • Adding your chain to the Vera database
        • Programmatic verification via the API
    • News & Media
      • Newsletter & Blog
    • Funding
      • L2 Funding Proposal
        • Aux Funding Images
    • Roadmap
  • 🙎Using Blockscout
    • Getting Started
      • Glossary of Terms
      • Main Menu
      • Blocks
      • Transaction Types
      • Beacon Chain Withdrawal Views
    • My Account
      • Watch list
      • Private tags
      • Public tags
      • API keys
      • Custom ABI
      • Verified addresses
        • Copy and Sign Message
      • For developers
    • Dappscout Apps Marketplace
      • DApp Integration
    • Swapscout
      • Earn Merits when using Swapscout
    • Revokescout
    • Autoscout Explorer Launchpad
    • CSV Exports
    • Token Support
      • ERC-1155 Support
    • Merits
      • Streak rewards
      • Activity Pass
      • Leaderboard
      • Badges
      • FAQs
  • 👩‍💻Developer Support
    • For Web3 Developers
    • Link to Blockscout
    • Blockscout APIs
      • Requests & Limits
      • REST API Endpoints
        • Stats API
        • Interpreter API
      • JSON RPC & ETH Compatible RPC Endpoints
        • Account
        • Block
        • Contract
        • Logs
        • Stats
        • Token
        • Transaction
        • ETH RPC API
      • GraphQL in Blockscout
    • Smart Contract Verification
      • Blockscout UI
      • Blockscout smart-contract verification API
      • Hardhat Verification Plugin
        • Sourcify Plugin for Hardhat
      • Foundry Verification
      • Sourcify Verification
      • OpenZeppelin Contract Verification
      • Automate verifications with Catapulta
      • Verification via thirdweb
      • Interacting with Smart Contracts
    • Blockscout SDK
    • Integrate Merits
    • Chainscout chains list
  • 🏃‍♂️Setup and Run Blockscout
    • General Overview
      • Separate Indexer, Web App, and API
      • Umbrella Project Organization
      • Indexer Architecture Overview
      • ShareLock
      • EVM Version Information
    • Requirements
      • General Backend Requirements / Blockscout Backend Prerequisites
      • Backend/Frontend Compatibility Matrix
      • Hardware & Hosting Requirements
      • Database Storage Requirements
      • Client Setting Requirements
      • Node Tracing / JSON RPC Requirements
      • L2 -> L1 JSON-RPC Method Requests
    • ☑️ENV Variables
      • Backend ENVs: Common
      • Backend ENVs: Chain-Specific
      • Backend ENVs: Integrations
      • Frontend ENVs: Common
        • ENVs
        • Deprecated ENVs
      • Deprecated Backend ENVs
        • Previous ENV Variable Home Page
    • Deployment
      • ⭐Manual Deployment Guide
        • Ubuntu Setup
        • MacOS setup
      • ⭐Docker-compose Deployment
      • ⭐Kubernetes Deployment
      • Rollup Deployment
      • 🌟Cosmos-based chains
      • 🍀Upgrade Guide (v7.0 & v8.0)
      • Frontend Migration
        • All-In-One Container
        • Separate Frontend
        • Customized Backend
        • Proxy Setup
        • FAQs
      • Manual Deployment (backend + old UI)
        • Manual cleaning an instance from the previous deployment
      • Terraform Deployment
      • Ansible Deployment (AWS Cloud)
        • Overview
        • Prerequisites
        • AWS Permissions & Settings
          • Creating a Secret Key Pair
          • Login with AWS CLI
          • Creating an AWS certificate for SSL
          • Manually Cleaning Terraform Related Instances
        • Variables
        • Deploying the Blockscout Infrastructure
        • Deploying Blockscout
        • Destroying Provisioned Infrastructure
        • Common Additional Tasks
        • Common Errors and Questions
        • AWS Marketplace (deprecated)
          • Overview
          • CloudFormation Template
          • Prerequisites & Install Parameters
          • Install from AWS Marketplace
          • AWS EC2 archive node setup with OpenEthereum (formerly Parity)
          • Updating & Redeploying in AWS
          • Customizing CSS
    • Microservices
      • Blockscout ENS (BENS) Name Service Integration
      • Smart Contract Verification
    • Configuration Options
      • Admin Panel Usage
      • Automating Restarts
      • Branding Configs
      • Circle CI Updates
      • Charts and Stats
      • CSS Configuration & Presets
      • Exchange Rates
      • Front-end Config Files
      • haproxy Settings for Blockscout.com
      • Internationalization
      • Logger Configs
      • Memory Usage
      • Metrics
      • My Account Settings
      • Sorting and Pagination
      • Tracing
      • Reown Project ID for contract Read/Write
    • Indexing
      • How do I fix indexer timeouts?
      • How do I update memory consumption to fix indexer memory errors?
    • Testing
    • DB schema
  • FAQs
    • User FAQs
    • Developer FAQs
  • 🧩Resources
    • EaaS: Hosting with Blockscout
    • Contributing to Blockscout
    • Bug Bounty Program
    • Media kit
    • Release Notes
      • v5.3.0: 10/23/23
      • v5.2.0: 6/20/23
      • v5.1.0: 2/13/23
      • v5.0.0: 1/11/23
    • Discord Channel
    • Discussion
    • GitHub Repo
Powered by GitBook
LogoLogo

Privacy and Terms

  • Privacy Notice
  • Terms and Conditions

Copyright © Blockscout Limited 2023-2024

On this page
  • Hardhat
  • Foundry (forge)
  • Remix
  • Flattener Plugin
  • Manual Standard Input Json with Remix

Was this helpful?

Export as PDF
  1. Developer Support
  2. Smart Contract Verification

OpenZeppelin Contract Verification

Instructions for various development environments

Last updated 10 months ago

Was this helpful?

Page partially based on .

Due to compiler constraints, contracts which include OpenZeppelin-related source contracts (e.g., import@openzeppelin/contracts/access/Ownable.sol;) can cause problems with verification. The sources must be provided explicitly during the contract verification process.

Below we describe verification information when including these contracts specific to different development environments.

Hardhat

is a full-featured development environment for contract compilation, deployment, and verification. The supports contract verification on Blockscout and includes in-built functionality to address verification with OpenZeppelin-based imports.

A separate tutorial about contract verification via Hardhat on Blockscout .

Foundry (forge)

is a smart contract development toolchain. Foundry manages your dependencies, compiles your project, runs tests, deploys, and lets you interact with the chain from the command line and via Solidity scripts.

Forge is a command-line tool that ships with Foundry. Forge tests, builds, and deploys your smart contracts. Forge supports contract verification out of the box ()

A separate tutorial about contract verification via Foundry on Blockscout .

Remix

While there are 2 plugins available to help with this process, Etherscan and Flattener. Currently only Flattener works with Blockscout. You can use this plugin to flatten contracts and submit for verification as flattened source code. This is the recommended option.

We also include the Manual Standard Input JSON Method below the flattener instructions. This method is not recommended, but included since it is also a viable method.

Flattener Plugin

Manual Standard Input Json with Remix

Standard input json is basically the raw file fed into the Solidity compiler on verification. There are some minor updates made regarding info the output compiler should return, but the main contract-related fields remain the same. This way, standard json allows you to specify the most subtle compiler settings, as well as to specify all source files.

It is quite cumbersome and tedious work, so it is preferable to use any of the above methods, however, we describe the process in more detail for those who want to use it.

For this tutorial, we will create standard json for the following contract:

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

import "@openzeppelin/contracts/access/Ownable.sol";

contract OpenZeppelinSample is Ownable {
    uint256 public a;

    constructor(uint256 _a) {
        a = _a;
    }

    function set(uint256 _a) public {
        a = _a;
    }
}

1) Start by creating a template which will be filled with sources later. For this purpose, we will use a metadata file generated by the compiler. You can find it in the “Compilation Details” tab (ensure that correct contract is chosen).

{
    "compiler": {
        "version": "0.8.7+commit.e28d00a7"
    },
    "language": "Solidity",
    "output": {
        "abi": [
            {
                "inputs": [
                    {
                        "internalType": "uint256",
                        "name": "_a",
                        "type": "uint256"
                    }
                ],
                "stateMutability": "nonpayable",
                "type": "constructor"
            },
            {
                "anonymous": false,
                "inputs": [
                    {
                        "indexed": true,
                        "internalType": "address",
                        "name": "previousOwner",
                        "type": "address"
                    },
                    {
                        "indexed": true,
                        "internalType": "address",
                        "name": "newOwner",
                        "type": "address"
                    }
                ],
                "name": "OwnershipTransferred",
                "type": "event"
            },
            {
                "inputs": [],
                "name": "a",
                "outputs": [
                    {
                        "internalType": "uint256",
                        "name": "",
                        "type": "uint256"
                    }
                ],
                "stateMutability": "view",
                "type": "function"
            },
            {
                "inputs": [],
                "name": "owner",
                "outputs": [
                    {
                        "internalType": "address",
                        "name": "",
                        "type": "address"
                    }
                ],
                "stateMutability": "view",
                "type": "function"
            },
            {
                "inputs": [],
                "name": "renounceOwnership",
                "outputs": [],
                "stateMutability": "nonpayable",
                "type": "function"
            },
            {
                "inputs": [
                    {
                        "internalType": "uint256",
                        "name": "_a",
                        "type": "uint256"
                    }
                ],
                "name": "set",
                "outputs": [],
                "stateMutability": "nonpayable",
                "type": "function"
            },
            {
                "inputs": [
                    {
                        "internalType": "address",
                        "name": "newOwner",
                        "type": "address"
                    }
                ],
                "name": "transferOwnership",
                "outputs": [],
                "stateMutability": "nonpayable",
                "type": "function"
            }
        ],
        "devdoc": {
            "kind": "dev",
            "methods": {
                "owner()": {
                    "details": "Returns the address of the current owner."
                },
                "renounceOwnership()": {
                    "details": "Leaves the contract without owner. It will not be possible to call `onlyOwner` functions anymore. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby removing any functionality that is only available to the owner."
                },
                "transferOwnership(address)": {
                    "details": "Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner."
                }
            },
            "version": 1
        },
        "userdoc": {
            "kind": "user",
            "methods": {},
            "version": 1
        }
    },
    "settings": {
        "compilationTarget": {
            "contracts/OpenZeppelinSample.sol": "OpenZeppelinSample"
        },
        "evmVersion": "london",
        "libraries": {},
        "metadata": {
            "bytecodeHash": "ipfs"
        },
        "optimizer": {
            "enabled": false,
            "runs": 200
        },
        "remappings": []
    },
    "sources": {
        "@openzeppelin/contracts/access/Ownable.sol": {
            "keccak256": "0xa94b34880e3c1b0b931662cb1c09e5dfa6662f31cba80e07c5ee71cd135c9673",
            "license": "MIT",
            "urls": [
                "bzz-raw://40fb1b5102468f783961d0af743f91b9980cf66b50d1d12009f6bb1869cea4d2",
                "dweb:/ipfs/QmYqEbJML4jB1GHbzD4cUZDtJg5wVwNm3vDJq1GbyDus8y"
            ]
        },
        "@openzeppelin/contracts/utils/Context.sol": {
            "keccak256": "0xe2e337e6dde9ef6b680e07338c493ebea1b5fd09b43424112868e9cc1706bca7",
            "license": "MIT",
            "urls": [
                "bzz-raw://6df0ddf21ce9f58271bdfaa85cde98b200ef242a05a3f85c2bc10a8294800a92",
                "dweb:/ipfs/QmRK2Y5Yc6BK7tGKkgsgn3aJEQGi5aakeSPZvS65PV8Xp3"
            ]
        },
        "contracts/OpenZeppelinSample.sol": {
            "keccak256": "0xca4fea32c78a11ca048ac26aa8655073e7a6ae534459ac4d07afbb55c9e751e0",
            "license": "GPL-3.0",
            "urls": [
                "bzz-raw://8c5e775b4b8c2c445d056d2ff6adc97d258fdf86071be30dcb3b13f09d143efb",
                "dweb:/ipfs/QmauZE2ySRS9Xm9JF7FsfzzGitEjHfSxnykyxvMRsSF7Lr"
            ]
        }
    },
    "version": 1
}sol

2) The only fields required for standard json input are “language”, “settings”, and “sources”. In addition, only content is needed for sources. Add it for each source and remove everything unnecessary.

{
    "language": "Solidity",
    "settings": {
        "compilationTarget": {
            "contracts/OpenZeppelinSample.sol": "OpenZeppelinSample"
        },
        "evmVersion": "london",
        "libraries": {},
        "metadata": {
            "bytecodeHash": "ipfs"
        },
        "optimizer": {
            "enabled": false,
            "runs": 200
        },
        "remappings": []
    },
    "sources": {
        "@openzeppelin/contracts/access/Ownable.sol": {
            "content": ""
        },
        "@openzeppelin/contracts/utils/Context.sol": {
            "content": ""
        },
        "contracts/OpenZeppelinSample.sol": {
            "content": ""
        }
    }
}

Note that the only fields to fill in the contents for each source file. Everything else (including compiler settings) is already filled via the metadata file.

3) First, add the content for the main OpenZeppelinSample.sol file

{
    "language": "Solidity",
    "settings": {
        "compilationTarget": {
            "contracts/OpenZeppelinSample.sol": "OpenZeppelinSample"
        },
        "evmVersion": "london",
        "libraries": {},
        "metadata": {
            "bytecodeHash": "ipfs"
        },
        "optimizer": {
            "enabled": false,
            "runs": 200
        },
        "remappings": []
    },
    "sources": {
        "@openzeppelin/contracts/access/Ownable.sol": {
            "content": ""
        },
        "@openzeppelin/contracts/utils/Context.sol": {
            "content": ""
        },
        "contracts/OpenZeppelinSample.sol": {
            "content": "// SPDX-License-Identifier: GPL-3.0\n\npragma solidity >=0.7.0 <0.9.0;\n\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\n\ncontract OpenZeppelinSample is Ownable {\n    uint256 public a;\n\n    constructor(uint256 _a) {\n        a = _a;\n    }\n\n    function set(uint256 _a) public {\n        a = _a;\n    }\n}"
        }
    }
}

4) Next, add the sources for @openzeppelin/contracts/access/Ownable.sol contract that is being imported. They can be found inside the .deps/npm/ directories, which will appear after contract compilation.

Copy the source code with the path used in imports:

{
    "language": "Solidity",
    "settings": {
        "compilationTarget": {
            "contracts/OpenZeppelinSample.sol": "OpenZeppelinSample"
        },
        "evmVersion": "london",
        "libraries": {},
        "metadata": {
            "bytecodeHash": "ipfs"
        },
        "optimizer": {
            "enabled": false,
            "runs": 200
        },
        "remappings": []
    },
    "sources": {
        "@openzeppelin/contracts/access/Ownable.sol": {
            "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n    address private _owner;\n\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n    /**\n     * @dev Initializes the contract setting the deployer as the initial owner.\n     */\n    constructor() {\n        _transferOwnership(_msgSender());\n    }\n\n    /**\n     * @dev Throws if called by any account other than the owner.\n     */\n    modifier onlyOwner() {\n        _checkOwner();\n        _;\n    }\n\n    /**\n     * @dev Returns the address of the current owner.\n     */\n    function owner() public view virtual returns (address) {\n        return _owner;\n    }\n\n    /**\n     * @dev Throws if the sender is not the owner.\n     */\n    function _checkOwner() internal view virtual {\n        require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n    }\n\n    /**\n     * @dev Leaves the contract without owner. It will not be possible to call\n     * `onlyOwner` functions anymore. Can only be called by the current owner.\n     *\n     * NOTE: Renouncing ownership will leave the contract without an owner,\n     * thereby removing any functionality that is only available to the owner.\n     */\n    function renounceOwnership() public virtual onlyOwner {\n        _transferOwnership(address(0));\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Can only be called by the current owner.\n     */\n    function transferOwnership(address newOwner) public virtual onlyOwner {\n        require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n        _transferOwnership(newOwner);\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Internal function without access restriction.\n     */\n    function _transferOwnership(address newOwner) internal virtual {\n        address oldOwner = _owner;\n        _owner = newOwner;\n        emit OwnershipTransferred(oldOwner, newOwner);\n    }\n}"
        },
        "@openzeppelin/contracts/utils/Context.sol": {
            "content": ""
        },
        "contracts/OpenZeppelinSample.sol": {
            "content": "// SPDX-License-Identifier: GPL-3.0\n\npragma solidity >=0.7.0 <0.9.0;\n\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\n\ncontract OpenZeppelinSample is Ownable {\n    uint256 public a;\n\n    constructor(uint256 _a) {\n        a = _a;\n    }\n\n    function set(uint256 _a) public {\n        a = _a;\n    }\n}"
        }
    }
}

5) The last source is a "../utils/Context.sol" imported from @openzeppelin/contracts/access/Ownable.sol. You can find it in the same .deps/npm directory.

Although import "../utils/Context.sol"; specifies a relative path, the absolute path is used in standard json, and can be found relative to @openzeppelin/contracts/access/Ownable.sol

    {
    "language": "Solidity",
    "settings": {
        "compilationTarget": {
            "contracts/OpenZeppelinSample.sol": "OpenZeppelinSample"
        },
        "evmVersion": "london",
        "libraries": {},
        "metadata": {
            "bytecodeHash": "ipfs"
        },
        "optimizer": {
            "enabled": false,
            "runs": 200
        },
        "remappings": []
    },
    "sources": {
        "@openzeppelin/contracts/access/Ownable.sol": {
            "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n    address private _owner;\n\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n    /**\n     * @dev Initializes the contract setting the deployer as the initial owner.\n     */\n    constructor() {\n        _transferOwnership(_msgSender());\n    }\n\n    /**\n     * @dev Throws if called by any account other than the owner.\n     */\n    modifier onlyOwner() {\n        _checkOwner();\n        _;\n    }\n\n    /**\n     * @dev Returns the address of the current owner.\n     */\n    function owner() public view virtual returns (address) {\n        return _owner;\n    }\n\n    /**\n     * @dev Throws if the sender is not the owner.\n     */\n    function _checkOwner() internal view virtual {\n        require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n    }\n\n    /**\n     * @dev Leaves the contract without owner. It will not be possible to call\n     * `onlyOwner` functions anymore. Can only be called by the current owner.\n     *\n     * NOTE: Renouncing ownership will leave the contract without an owner,\n     * thereby removing any functionality that is only available to the owner.\n     */\n    function renounceOwnership() public virtual onlyOwner {\n        _transferOwnership(address(0));\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Can only be called by the current owner.\n     */\n    function transferOwnership(address newOwner) public virtual onlyOwner {\n        require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n        _transferOwnership(newOwner);\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Internal function without access restriction.\n     */\n    function _transferOwnership(address newOwner) internal virtual {\n        address oldOwner = _owner;\n        _owner = newOwner;\n        emit OwnershipTransferred(oldOwner, newOwner);\n    }\n}"
        },
        "@openzeppelin/contracts/utils/Context.sol": {
            "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n    function _msgSender() internal view virtual returns (address) {\n        return msg.sender;\n    }\n\n    function _msgData() internal view virtual returns (bytes calldata) {\n        return msg.data;\n    }\n}"
        },
        "contracts/OpenZeppelinSample.sol": {
            "content": "// SPDX-License-Identifier: GPL-3.0\n\npragma solidity >=0.7.0 <0.9.0;\n\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\n\ncontract OpenZeppelinSample is Ownable {\n    uint256 public a;\n\n    constructor(uint256 _a) {\n        a = _a;\n    }\n\n    function set(uint256 _a) public {\n        a = _a;\n    }\n}"
        }
    }
}

6) Submit Standard JSON input for verification.

Go to Plugin Manager and search for "flattener". Activate the plugin. \

The plugin is now available in the lefthand menu. \

Compile the contract you want to verify. \

Return to the Flattener. The ‘Flatten contract_name’ button should appear. Click the button. Flattened source code is copied onto the clipboard. \

Go to Blockscout and on the verification page choose the ‘Via flattened source code’ method. \

Paste the copied flattened source code into the ‘Enter the Solidity Contract Code’ field. \

Check that all info is correct and click the Verify and Publish button to verify your contract. Once verified, the code tab will include the icon and source code will be viewable.

👩‍💻
OpenZeppelin Tutorial
Hardhat
Hardhat Verification plugin
is available here
Foundry
https://book.getfoundry.sh/reference/forge/forge-verify-contract
is available here
Hardhat
Foundry (forge)
Remix
✅
Compilation Details → Metadata