# Verify through Hardhat

This guide covers Hardhat 2.x for deploying and verifying smart contracts on SocialScan-supported chains (Monad, Pharos, etc.).&#x20;

Hardhat is a smart contracts development tool, perfect if you're familiar with Javascript/Typescript.

{% hint style="info" %}
SocialScan doesn't require an API key for contract verification.
{% endhint %}

### Quick Start

#### Option 1: Use This Example Project

Clone this repository:

```bash
git clone https://github.com/HemeraProtocol/hardhat-verify-example.git                                                                                     
cd hardhat-verify-example                                                                                                                            
npm install      
```

Copy `.env.example` to `.env` and add your settings:

```bash
cp .env.example .env
# Edit .env with your private key and RPC URLs
```

#### Option 2: Add to Your Existing Project

If you already have a Hardhat 2.x project, follow the installation and configuration steps below.

### Install

Via npm:

```bash
npm install --save-dev @nomicfoundation/hardhat-verify
npm install --save-dev @nomicfoundation/hardhat-ignition
npm install --save-dev dotenv
```

For upgradeable contracts (optional):

```bash
npm install --save-dev @openzeppelin/hardhat-upgrades
npm install @openzeppelin/contracts-upgradeable
```

### Config

In `hardhat.config.ts`, import the plugins:

```typescript
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-ethers";
import "@nomicfoundation/hardhat-verify";
import "@nomicfoundation/hardhat-ignition";
import "@openzeppelin/hardhat-upgrades";
import "dotenv/config";

const config: HardhatUserConfig = {
  solidity: {
    version: "0.8.28",
    settings: {
      optimizer: {
        enabled: true,
        runs: 200,
      },
    },
  },
  networks: {
    sepolia: {
      url: process.env.SEPOLIA_RPC_URL || "",
      accounts: process.env.SEPOLIA_PRIVATE_KEY && process.env.SEPOLIA_PRIVATE_KEY.length >= 64
        ? [process.env.SEPOLIA_PRIVATE_KEY]
        : [],
    },
    monadTestnet: {
      url: process.env.MONAD_TESTNET_RPC_URL || "https://testnet-rpc.monad.xyz",
      accounts: process.env.PRIVATE_KEY && process.env.PRIVATE_KEY.length >= 64
        ? [process.env.PRIVATE_KEY]
        : [],
      chainId: 10143,
    },
    pharosTestnet: {
      url: process.env.PHAROS_TESTNET_RPC_URL || "",
      accounts: process.env.PRIVATE_KEY && process.env.PRIVATE_KEY.length >= 64
        ? [process.env.PRIVATE_KEY]
        : [],
      chainId: 688689,
    },
  },
  etherscan: {
    apiKey: {
      monadTestnet: process.env.ETHERSCAN_API_KEY || "test",
      pharosTestnet: process.env.ETHERSCAN_API_KEY || "test",
    },
    customChains: [
      {
        network: "monadTestnet",
        chainId: 10143,
        urls: {
          apiURL: "https://api.socialscan.io/monad-testnet/v1/explorer/command_api/contract",
          browserURL: "https://monad-testnet.socialscan.io",
        },
      },
      {
        network: "pharosTestnet",
        chainId: 688689,
        urls: {
          apiURL: "https://api.socialscan.io/pharos-atlantic-testnet/v1/explorer/command_api/contract",
          browserURL: "https://pharos-testnet.socialscan.io",
        },
      },
    ],
  },
};


export default config;
```

Create `.env`:

```bash
PRIVATE_KEY=0xYourPrivateKey
MONAD_TESTNET_RPC_URL=https://testnet-rpc.monad.xyz/
PHAROS_TESTNET_RPC_URL=https://atlantic.dplabs-internal.com
```

### Deploy and Verify (using Hardhat Ignition)

Deploy to Monad Testnet:

```bash
# NOTE: Must change --deployment-id value before each deployment
# Use unique identifiers (deployment-1, deployment-2, deployment-3, etc.)
# This guarantees each deployment gets a unique address without conflicts
npx hardhat ignition deploy ignition/modules/Counter.ts --network monadTestnet --verify --deployment-id deployment-1
```

Deploy to Pharos Testnet:

<pre class="language-bash"><code class="lang-bash"># NOTE: Must change --deployment-id value before each deployment
# Use unique identifiers (deployment-1, deployment-2, deployment-3, etc.)
# This guarantees each deployment gets a unique address without conflicts
<strong>npx hardhat ignition deploy ignition/modules/Counter.ts --network pharosTestnet --verify --deployment-id deployment-1
</strong></code></pre>

### Verify an Existing Contract

```bash
npx hardhat verify --network monadTestnet 0x8A751D07341f7C22c4D131CF23eaA6fdD6e5D89f
```

With constructor arguments:

```bash
npx hardhat verify --network monadTestnet 0x8A751D07341f7C22c4D131CF23eaA6fdD6e5D89f 100
```

### Upgradeable Contracts (UUPS)

First, add the upgrades plugin to your config:

```typescript
import "@openzeppelin/hardhat-upgrades";
```

#### Deploy Proxy

Create `scripts/deploy-proxy.ts`:

```typescript
import { ethers, upgrades, run } from "hardhat";

async function main() {
  const BoxV1 = await ethers.getContractFactory("BoxV1");
  const proxy = await upgrades.deployProxy(BoxV1, [], {
    initializer: "initialize",
    kind: "uups",
  });

  await proxy.waitForDeployment();
  const proxyAddress = await proxy.getAddress();
  const implementationAddress = await upgrades.erc1967.getImplementationAddress(proxyAddress);

  console.log("Proxy:", proxyAddress);
  console.log("Implementation:", implementationAddress);

  // Verify implementation
  await new Promise(resolve => setTimeout(resolve, 30000)); // Wait for indexing

  await run("verify:verify", {
    address: implementationAddress,
    constructorArguments: [],
  });
}

main();
```

Deploy:

```bash
npx hardhat run scripts/deploy-proxy.ts --network monadTestnet
```

#### Upgrade Proxy

Create `scripts/upgrade-proxy.ts`:

```typescript
import { ethers, upgrades, run } from "hardhat";

async function main() {
  const proxyAddress = process.env.PROXY_ADDRESS;
  const BoxV2 = await ethers.getContractFactory("BoxV2");

  await upgrades.upgradeProxy(proxyAddress, BoxV2);

  const newImplAddress = await upgrades.erc1967.getImplementationAddress(proxyAddress);
  console.log("New Implementation:", newImplAddress);

  // Verify new implementation
  await new Promise(resolve => setTimeout(resolve, 30000));

  await run("verify:verify", {
    address: newImplAddress,
    constructorArguments: [],
  });
}

main();
```

Upgrade:

```bash
PROXY_ADDRESS=0xYourProxyAddress npx hardhat run scripts/upgrade-proxy.ts --network monadTestnet
```

### Proxy Verification Limitations

**Important:** The proxy contract itself (ERC1967Proxy from OpenZeppelin) cannot be automatically verified using the Hardhat verify plugin.

**What gets verified:**

* ✅ Implementation contracts (BoxV1, BoxV2, etc.) - Automatically verified
* ✅ All your business logic is visible to users

**What doesn't get verified:**

* ⚠️ The proxy contract (ERC1967Proxy)

**Why this happens:**

* The proxy contract is in `node_modules/@openzeppelin/contracts/`
* Hardhat verify plugin doesn't handle library contracts well
* This is a plugin limitation, not SocialScan-specific

**Why this is acceptable:**

* Users can see all source code via the verified implementation
* The proxy is a standard, audited OpenZeppelin contract
* Functionality is completely unaffected

### Live Examples

Monad Testnet:

* Simple contract: [0x8A751D07...](https://monad-testnet.socialscan.io/address/0x8A751D07341f7C22c4D131CF23eaA6fdD6e5D89f#code) ✅ Verified
* Proxy implementation: [0x846e12dD...](https://monad-testnet.socialscan.io/address/0x846e12dD4cc81BB8da9CF5cc6c1D8b03C3179973#code) ✅ Verified

Pharos Atlantic Testnet:

* Proxy: 0xE86E25D18F9C3f41Ef123f29aD29165a7F3c7F43
* Implementation V1: [0xf9018fB7...](https://pharos-testnet.socialscan.io/address/0xf9018fB711B2dd641DC55CFE2c60787c4C243F2D#code) ✅ Verified
* Implementation V2: [0x43503c53...](https://pharos-testnet.socialscan.io/address/0x43503c53501C0497E803235679F1ed34F0d43771#code) ✅ Verified


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://thehemera.gitbook.io/explorer-api/verify-smart-contract/verify-smart-contract/verify-through-hardhat.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
