> ## Documentation Index
> Fetch the complete documentation index at: https://docs.plasma.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Verify a Contract

> This guide shows how to verify smart contracts on Plasma using Foundry, Hardhat, and Ethers.js. Verification makes contract source code publicly accessible on block explorers and ensures transparency for users.

## Prerequisites

* A deployed contract on Plasma testnet. See [deploy a contract](./deploy-a-contract.mdx) if you haven't deployed one yet.
* Your contract's source code and compilation settings.
* Constructor arguments used during deployment (if any).

The Plasma block explorer (`plasmascan.to`) serves an Etherscan-compatible verification API. The examples below use that API directly — no third-party account is required to submit a verification.

## Example Contracts

We'll use two example contracts to demonstrate both simple and complex constructor arguments:

### Simple Contract

A basic data storage contract that demonstrates fundamental smart contract concepts.

```solidity theme={null}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

contract SimpleStorage {
    string private storedData;
    address public owner;
    
    constructor(string memory initialData) {
        storedData = initialData;
        owner = msg.sender;
    }
    
    function setData(string memory data) public {
        require(msg.sender == owner, "Only owner can set data");
        storedData = data;
    }
    
    function getData() public view returns (string memory) {
        return storedData;
    }
}
```

### Complex Contract

A more sophisticated vault system for managing token deposits with configurable parameters.

```solidity theme={null}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

contract TokenVault {
    struct VaultConfig {
        uint256 minDeposit;
        uint256 maxDeposit;
        bool isActive;
    }
    
    address[] public authorisedTokens;
    VaultConfig public config;
    mapping(address => uint256) public balances;
    
    constructor(
        address[] memory _tokens,
        uint256 _minDeposit,
        uint256 _maxDeposit,
        bool _isActive
    ) {
        authorisedTokens = _tokens;
        config = VaultConfig(_minDeposit, _maxDeposit, _isActive);
    }
    
    function deposit(address token, uint256 amount) external {
        require(config.isActive, "Vault is not active");
        require(amount >= config.minDeposit, "Amount below minimum");
        require(amount <= config.maxDeposit, "Amount above maximum");
        balances[msg.sender] += amount;
    }
}
```

## Verify with Foundry

[Foundry](https://getfoundry.sh) provides the `forge verify-contract` command for contract verification.

1. Ensure your `foundry.toml` includes the verification settings:

   ```toml theme={null}
   [profile.default]
   src = "src"
   out = "out"
   libs = ["lib"]
   solc_version = "0.8.28"
   optimizer = true
   optimizer_runs = 200

   [rpc_endpoints]
   plasma_testnet = "https://testnet-rpc.plasma.to"
   ```

2. Verify the contract. For the SimpleStorage contract with a string constructor argument:

   ```shell theme={null}
   forge verify-contract \
     --chain-id 9746 \
     --num-of-optimizations 200 \
     --watch \
     --constructor-args $(cast abi-encode "constructor(string)" "Hello, Plasma!") \
     --compiler-version v0.8.28+commit.7893614a \
     0x742d35Cc6610C7532C8582d4C371Acb1D5f44D7F \
     src/SimpleStorage.sol:SimpleStorage
   ```

   For the TokenVault contract with multiple constructor arguments:

   ```shell theme={null}
   # First, encode the constructor arguments.
   CONSTRUCTOR_ARGS=$(cast abi-encode \
       "constructor(address[],uint256,uint256,bool)" \
       "[0x1234567890123456789012345678901234567890,0x0987654321098765432109876543210987654321]" \
       1000000000000000000 \
       10000000000000000000 \
       true)

   forge verify-contract \
       --chain-id 9746 \
       --num-of-optimizations 200 \
       --watch \
       --constructor-args $CONSTRUCTOR_ARGS \
       --compiler-version v0.8.28+commit.7893614a \
       0x9876543210987654321098765432109876543210 \
       src/TokenVault.sol:TokenVault
   ```

3. Check verification status. Foundry will show real-time verification status with the `--watch` flag. Look for:

   ```plaintext theme={null}
   Submitting verification for [src/SimpleStorage.sol:SimpleStorage] 0x742d35Cc6610C7532C8582d4C371Acb1D5f44D7F.
   Submitted contract for verification:
           Response: `OK`
           GUID: `abc123def456ghi789`
           URL: https://testnet.plasmascan.to/address/0x742d35Cc6610C7532C8582d4C371Acb1D5f44D7F
   Contract verification status:
   Response: `NOTOK`
   Details: `Pending in queue`
   Contract verification status:
   Response: `OK`
   Details: `Pass - Verified`
   Contract successfully verified
   ```

## Verify with Hardhat

[Hardhat](https://hardhat.org/) offers contract verification through the `@nomicfoundation/hardhat-verify` plugin.

1. Update your `hardhat.config.js` to include verification settings:

   ```javascript theme={null}
   require("@nomicfoundation/hardhat-toolbox");
   require("@nomicfoundation/hardhat-verify");
   require("dotenv").config();

   module.exports = {
     solidity: {
       version: "0.8.28",
       settings: {
         optimizer: {
           enabled: true,
           runs: 200
         }
       }
     },
     networks: {
       plasmaTestnet: {
         url: process.env.RPC_URL,
         chainId: 9746,
         accounts: [process.env.PRIVATE_KEY]
       }
     },
     etherscan: {
       customChains: [
         {
           network: "plasmaTestnet",
           chainId: 9746,
           urls: {
             apiURL: "https://testnet.plasmascan.to/api",
             browserURL: "https://testnet.plasmascan.to/"
           }
         }
       ]
     },
     sourcify: {
       enabled: false
     }
   };
   ```

2. Verify the contract. For the simple contract:

   ```shell theme={null}
   npx hardhat verify \
       --network plasmaTestnet \
       0x742d35Cc6610C7532C8582d4C371Acb1D5f44D7F \
       "Hello, Plasma!"
   ```

   For contracts with multiple constructor arguments, create a verification script `scripts/verify.js`:

   ```javascript theme={null}
   const hre = require("hardhat");

   async function main() {
     const contractAddress = "0x9876543210987654321098765432109876543210";
     
     // Constructor arguments for TokenVault.
     const constructorArgs = [
       [
         "0x1234567890123456789012345678901234567890",
         "0x0987654321098765432109876543210987654321"
       ], // address[] _tokens
       "1000000000000000000", // uint256 _minDeposit (1e18, assuming 18 decimals)
       "10000000000000000000", // uint256 _maxDeposit (1e19, assuming 18 decimals)
       true // bool _isActive
     ];

     try {
       await hre.run("verify:verify", {
         address: contractAddress,
         constructorArguments: constructorArgs,
       });
       console.log("Contract verified successfully!");
     } catch (error) {
       console.error("Verification failed:", error);
     }
   }

   main()
     .then(() => process.exit(0))
     .catch((error) => {
       console.error(error);
       process.exit(1);
     });
   ```

3. Run the verification script:

   ```shell theme={null}
   npx hardhat run scripts/verify.js --network plasmaTestnet
   ```

### Automated Verification During Deployment

You can also verify contracts automatically during deployment by adding verification to your deployment script:

```javascript theme={null}
const { ethers } = require("hardhat");

async function main() {
  const SimpleStorage = await ethers.getContractFactory("SimpleStorage");
  const simpleStorage = await SimpleStorage.deploy("Hello, Plasma!");
  
  await simpleStorage.waitForDeployment();
  const contractAddress = await simpleStorage.getAddress();
  
  console.log("SimpleStorage deployed to:", contractAddress);
  
  // Wait for a few block confirmations before verifying.
  console.log("Waiting for block confirmations...");
  await simpleStorage.deploymentTransaction().wait(5);
  
  // Verify the contract.
  try {
    await hre.run("verify:verify", {
      address: contractAddress,
      constructorArguments: ["Hello, Plasma!"],
    });
    console.log("Contract verified successfully!");
  } catch (error) {
    console.log("Verification failed:", error.message);
  }
}

main();
```

## Verify with Ethers.js

[Ethers.js](https://ethers.org/) requires manual API calls to the block explorer for verification. This approach gives you full control over the verification process.

1. Install the required dependencies:

   ```shell theme={null}
   npm install ethers dotenv axios form-data
   ```

2. Create verification script `verify-ethers.js`:

   ```javascript theme={null}
   const axios = require('axios');
   const fs = require('fs');
   require('dotenv').config();

   async function verifyContract(contractAddress, sourceCode, contractName, constructorArgs = "") {
     const apiUrl = "https://testnet.plasmascan.to/api";
     
     const data = {
       module: 'contract',
       action: 'verifysourcecode',
       contractaddress: contractAddress,
       sourceCode: sourceCode,
       codeformat: 'solidity-single-file',
       contractname: contractName,
       compilerversion: 'v0.8.28+commit.7893614a',
       optimizationUsed: '1',
       runs: '200',
       constructorArguements: constructorArgs, // Note: API uses this spelling.
       evmversion: 'default',
       licenseType: '3' // MIT License
     };

     try {
       console.log('Submitting contract for verification...');
       const response = await axios.post(apiUrl, new URLSearchParams(data));
       
       if (response.data.status === '1') {
         const guid = response.data.result;
         console.log('Verification submitted successfully!');
         console.log('GUID:', guid);
         
         // Check verification status.
         await checkVerificationStatus(guid);
       } else {
         console.error('Verification submission failed:', response.data.result);
       }
     } catch (error) {
       console.error('Error submitting verification:', error.message);
     }
   }

   async function checkVerificationStatus(guid) {
     const apiUrl = "https://testnet.plasmascan.to/api";
     const maxAttempts = 30;
     let attempts = 0;

     while (attempts < maxAttempts) {
       try {
         const response = await axios.get(apiUrl, {
           params: {
             module: 'contract',
             action: 'checkverifystatus',
             guid: guid
           }
         });

         const status = response.data.status;
         const result = response.data.result;

         if (status === '1') {
           console.log('✅ Contract verified successfully!');
           console.log('Result:', result);
           break;
         } else if (result.includes('Fail')) {
           console.error('❌ Verification failed:', result);
           break;
         } else {
           console.log('⏳ Verification pending...');
           attempts++;
           
           if (attempts < maxAttempts) {
             await new Promise(resolve => setTimeout(resolve, 10000)); // Wait 10 seconds.
           }
         }
       } catch (error) {
         console.error('Error checking status:', error.message);
         break;
       }
     }

     if (attempts >= maxAttempts) {
       console.log('⚠️ Verification status check timed out. Please check manually.');
     }
   }

   // Verify SimpleStorage contract.
   async function verifySimpleStorage() {
     const contractAddress = "0x742d35Cc6610C7532C8582d4C371Acb1D5f44D7F";
     const sourceCode = fs.readFileSync('SimpleStorage.sol', 'utf8');
     const constructorArgs = "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20506c61736d612100000000000000000000000000000000000000";
     
     await verifyContract(contractAddress, sourceCode, "SimpleStorage", constructorArgs);
   }

   // Verify TokenVault contract.
   async function verifyTokenVault() {
     const contractAddress = "0x9876543210987654321098765432109876543210";
     const sourceCode = fs.readFileSync('TokenVault.sol', 'utf8');
     
     // Complex constructor arguments (ABI encoded).
     const constructorArgs = "0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000008ac7230489e8000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000123456789012345678901234567890123456789000000000000000000000000098765432109876543210987654321098765432100";
     
     await verifyContract(contractAddress, sourceCode, "TokenVault", constructorArgs);
   }

   // Run verification (uncomment the one you need).
   verifySimpleStorage();
   // verifyTokenVault();
   ```

3. Encode constructor arguments. For complex constructor arguments, you need to ABI encode them. You can use Foundry's `cast` tool:

   ```shell theme={null}
   # For SimpleStorage with string argument.
   cast abi-encode "constructor(string)" "Hello, Plasma!"

   # For TokenVault with complex arguments.
   cast abi-encode \
       "constructor(address[],uint256,uint256,bool)" \
       "[0x1234567890123456789012345678901234567890,0x0987654321098765432109876543210987654321]" \
       1000000000000000000 \
       10000000000000000000 \
       true
   ```

4. Finally, run the verification:

   ```shell theme={null}
   node verify-ethers.js
   ```

## Check Verification Status

After verification, you can check if it was successful:

### On Block Explorer

1. Visit [Plasma testnet explorer](https://testnet.plasmascan.to/).
2. Search for your contract address.
3. Look for a green checkmark next to "Contract" tab.
4. Click the "Contract" tab to view the verified source code.

### Programmatically

1. Create a status check script `check-verification.js`:

   ```javascript theme={null}
   const axios = require('axios');
   require('dotenv').config();

   async function checkIfVerified(contractAddress) {
     const apiUrl = "https://testnet.plasmascan.to/api";
     
     try {
       const response = await axios.get(apiUrl, {
         params: {
           module: 'contract',
           action: 'getsourcecode',
           address: contractAddress,
         }
       });

       const result = response.data.result[0];
       
       if (result.SourceCode && result.SourceCode !== '') {
         console.log('✅ Contract is verified!');
         console.log('Contract Name:', result.ContractName);
         console.log('Compiler Version:', result.CompilerVersion);
         console.log('Optimisation Used:', result.OptimizationUsed);
         return true;
       } else {
         console.log('❌ Contract is not verified');
         return false;
       }
     } catch (error) {
       console.error('Error checking verification status:', error.message);
       return false;
     }
   }

   // Check your contract.
   checkIfVerified("0x742d35Cc6610C7532C8582d4C371Acb1D5f44D7F");
   ```

## Alternative Methods

While block explorers are the most common verification method, you can also use:

### Sourcify

[Sourcify](https://sourcify.dev/) provides decentralised contract verification:

```shell theme={null}
# Install Sourcify CLI.
npm install -g @ethereum-sourcify/cli

# Verify contract.
sourcify verify \
    --network 9746 \
    --address 0x742d35Cc6610C7532C8582d4C371Acb1D5f44D7F \
    --files contracts/SimpleStorage.sol
```

### Tenderly

[Tenderly](https://tenderly.co/) offers verification as part of their debugging platform. See their [documentation](https://docs.tenderly.co/contract-verification) for details.

## Contract Flattening

For contracts with imports or libraries, you may need to flatten your contract into a single file before verification. Popular tools include:

* [Foundry's forge flatten](https://book.getfoundry.sh/reference/forge/forge-flatten)
* [Hardhat's flatten plugin](https://github.com/NomicFoundation/hardhat/tree/main/packages/hardhat-core#flattening-contracts)
* [Truffle Flattener](https://github.com/nomiclabs/truffle-flattener)

For complex projects with external libraries, see the [Hardhat verification documentation](https://hardhat.org/hardhat-runner/plugins/nomicfoundation-hardhat-verify) for advanced configuration options.

## Troubleshooting

### Constructor Argument Mismatch

```plaintext theme={null}
Error: Invalid constructor arguments provided
```

Double-check your constructor arguments match exactly what was used during deployment. Use `cast abi-encode` to verify encoding.

### Compiler Version Mismatch

```plaintext theme={null}
Error: Compilation failed
```

Ensure the compiler version in your verification request matches the version used during compilation.

### Optimisation Settings Mismatch

```plaintext theme={null}
Error: Bytecode doesn't match
```

Verify that optimisation settings (enabled/disabled and runs count) match your compilation settings.

### Already Verified

```plaintext theme={null}
Error: Contract source code already verified
```

This means verification was successful previously. Check the block explorer to confirm.

### Rate Limiting

```plaintext theme={null}
Error: Rate limit exceeded
```

Wait a few minutes before retrying. Consider upgrading your API plan if you need higher limits.
