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

# Verify Contracts

> Comprehensive guide on how to verify your EVM smart contracts on Sei

export const AddSeiButton = ({network = 'mainnet', label = 'Add Sei to MetaMask'}) => {
  const SEI_MAINNET_CHAIN_PARAMS = {
    chainId: '0x531',
    chainName: 'Sei Network',
    rpcUrls: ['https://evm-rpc.sei-apis.com'],
    nativeCurrency: {
      name: 'Sei',
      symbol: 'SEI',
      decimals: 18
    },
    blockExplorerUrls: ['https://seiscan.io']
  };
  const SEI_TESTNET_CHAIN_PARAMS = {
    chainId: '0x530',
    chainName: 'Sei Testnet',
    rpcUrls: ['https://evm-rpc-testnet.sei-apis.com'],
    nativeCurrency: {
      name: 'Sei',
      symbol: 'SEI',
      decimals: 18
    },
    blockExplorerUrls: ['https://testnet.seiscan.io']
  };
  const chainParams = network === 'testnet' ? SEI_TESTNET_CHAIN_PARAMS : SEI_MAINNET_CHAIN_PARAMS;
  const [status, setStatus] = useState(null);
  const [isHovered, setIsHovered] = useState(false);
  const [isBusy, setIsBusy] = useState(false);
  const addOrSwitchSeiNetwork = async params => {
    if (typeof window === 'undefined' || !window.ethereum) {
      throw new Error('MetaMask is not installed');
    }
    const ethereum = window.ethereum;
    try {
      await ethereum.request({
        method: 'wallet_switchEthereumChain',
        params: [{
          chainId: params.chainId
        }]
      });
    } catch (switchError) {
      if (switchError && switchError.code === 4902) {
        await ethereum.request({
          method: 'wallet_addEthereumChain',
          params: [params]
        });
      } else {
        throw switchError;
      }
    }
  };
  const onClick = async e => {
    e.preventDefault();
    setIsBusy(true);
    setStatus(null);
    try {
      await addOrSwitchSeiNetwork(chainParams);
      setStatus({
        type: 'success',
        message: `${chainParams.chainName} added or switched.`
      });
    } catch (err) {
      const message = err && err.message ? err.message : 'Failed to add or switch network.';
      setStatus({
        type: 'error',
        message
      });
    } finally {
      setIsBusy(false);
    }
  };
  return <span className="inline-flex flex-col items-start gap-1">
      <button type="button" onClick={onClick} disabled={isBusy} onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)} className="inline-flex items-center gap-1 px-3 py-1.5 text-white transition-colors min-w-[160px]" style={{
    backgroundColor: isHovered ? 'var(--sei-maroon-200)' : 'var(--sei-maroon-100)',
    color: '#ffffff',
    fontFamily: 'var(--sei-font-mono)',
    textTransform: 'uppercase',
    letterSpacing: '0.04em',
    fontSize: '10px',
    opacity: isBusy ? 0.7 : 1,
    cursor: isBusy ? 'default' : 'pointer'
  }}>
        {isBusy ? 'Adding…' : label}
      </button>
      {status && <span className={status.type === 'error' ? 'text-red-600 dark:text-red-400' : 'text-green-600 dark:text-green-400'} style={{
    fontSize: '11px'
  }}>
          {status.message}
        </span>}
    </span>;
};

export const SandboxEmbed = props => {
  const {src, kind = 'codesandbox', title, description, height, label} = props || ({});
  const KINDS = {
    codesandbox: {
      name: 'CodeSandbox',
      host: 'codesandbox.io',
      defaultHeight: 500
    },
    remix: {
      name: 'Remix IDE',
      host: 'remix.ethereum.org',
      defaultHeight: 620
    },
    stackblitz: {
      name: 'StackBlitz',
      host: 'stackblitz.com',
      defaultHeight: 500
    }
  };
  const meta = KINDS[kind] || KINDS.codesandbox;
  const parsedHeight = Number(height);
  const frameHeight = Number.isFinite(parsedHeight) && parsedHeight > 0 ? parsedHeight : meta.defaultHeight;
  const allowAttr = 'clipboard-read; clipboard-write';
  const [loaded, setLoaded] = useState(false);
  const [btnHover, setBtnHover] = useState(false);
  const HAIRLINE = 'rgba(128, 128, 128, 0.25)';
  const surfaceStyle = {
    backgroundColor: 'rgba(128, 128, 128, 0.08)'
  };
  const monoStyle = {
    fontFamily: 'var(--sei-font-mono)'
  };
  const cardClass = 'not-prose w-full rounded-lg border overflow-hidden my-4';
  const headerClass = 'flex items-center justify-between gap-3 px-4 py-2.5 border-b';
  const buttonStyle = {
    backgroundColor: btnHover ? 'var(--sei-maroon-200)' : 'var(--sei-maroon-100)',
    color: '#ffffff',
    fontFamily: 'var(--sei-font-mono)',
    textTransform: 'uppercase',
    letterSpacing: '0.04em',
    fontSize: '10px',
    cursor: 'pointer'
  };
  const PlayIcon = () => <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
			<path d="M8 5v14l11-7z" />
		</svg>;
  const ExternalIcon = () => <svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
			<path d="M14 5h5v5" />
			<path d="M19 5l-9 9" />
			<path d="M19 14v5a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V8a1 1 0 0 1 1-1h5" />
		</svg>;
  return <div className={cardClass} style={{
    borderColor: HAIRLINE
  }}>
			<div className={headerClass} style={{
    ...surfaceStyle,
    borderBottomColor: HAIRLINE
  }}>
				<div className="flex flex-col min-w-0">
					<span className="text-sm font-medium text-neutral-900 dark:text-white truncate" style={monoStyle}>
						{title || meta.name}
					</span>
					<span className="text-xs text-neutral-500 dark:text-neutral-500">{meta.name}</span>
				</div>
				<div className="flex items-center gap-3 shrink-0">
					{src ? <a href={src} target="_blank" rel="noopener noreferrer" className="inline-flex items-center gap-1 text-xs text-neutral-500 hover:text-neutral-800 dark:text-neutral-400 dark:hover:text-neutral-200 transition-colors">
							Open <ExternalIcon />
						</a> : null}
					{!loaded && src ? <button type="button" onClick={() => setLoaded(true)} onMouseEnter={() => setBtnHover(true)} onMouseLeave={() => setBtnHover(false)} className="inline-flex items-center gap-1.5 px-3 py-1.5 transition-colors" style={buttonStyle}>
							<PlayIcon />
							{label || 'Load editor'}
						</button> : null}
				</div>
			</div>

			{description ? <div className="px-4 pt-3 pb-1 text-sm text-neutral-600 dark:text-neutral-400">{description}</div> : null}

			{!src ? <div className="px-4 py-6 text-sm text-red-600 dark:text-red-400" style={monoStyle}>
					SandboxEmbed: missing required `src`.
				</div> : loaded ? <iframe src={src} title={title || meta.name} className="w-full block border-0" style={{
    height: frameHeight + 'px',
    backgroundColor: 'rgba(128, 128, 128, 0.05)'
  }} allow={allowAttr} loading="lazy" allowFullScreen /> : <button type="button" onClick={() => setLoaded(true)} className="w-full flex flex-col items-center justify-center gap-2 text-neutral-500 dark:text-neutral-400 hover:text-neutral-800 dark:hover:text-neutral-200 transition-colors" style={{
    height: frameHeight + 'px',
    cursor: 'pointer',
    ...surfaceStyle
  }}>
					<PlayIcon />
					<span className="text-sm" style={monoStyle}>Click to load {meta.name}</span>
					<span className="text-xs">Loads {meta.host} in an embedded editor</span>
				</button>}
		</div>;
};

Verify your deployed contract using flattened source code, JSON input, Sourcify and more. Verifying your deployed contract ensures transparency and trust by making the source code publicly available and verifiable.

## Benefits of Verification

* **Transparency**: Publicly available source code.
* **Trust**: Community can verify the contract's functionality.
* **Security**: Greater confidence in the contract's integrity.

## Sourcify

[Sourcify](https://sourcify.dev/) provides decentralized, open-source contract verification. Sei has full Sourcify support on both **mainnet** (chain ID 1329) and **testnet** (chain ID 1328).

Sourcify performs byte-by-byte verification by recompiling your source code with the exact same compiler settings and comparing the output against the on-chain bytecode. No API key is required.

### Verify with Foundry

After deploying your contract with Foundry, verify it on Sourcify:

```bash theme={"dark"}
forge verify-contract \
  --verifier sourcify \
  --chain-id <CHAIN_ID> \
  <DEPLOYED_CONTRACT_ADDRESS> \
  <PATH:CONTRACT_NAME>
```

**Testnet example:**

```bash theme={"dark"}
forge verify-contract \
  --verifier sourcify \
  --chain-id 1328 \
  0x1234567890abcdef1234567890abcdef12345678 \
  src/Counter.sol:Counter
```

**Mainnet example:**

```bash theme={"dark"}
forge verify-contract \
  --verifier sourcify \
  --chain-id 1329 \
  0x1234567890abcdef1234567890abcdef12345678 \
  src/Counter.sol:Counter
```

To check verification status:

```bash theme={"dark"}
forge verify-check \
  --verifier sourcify \
  --chain-id 1328 \
  <VERIFICATION_JOB_ID>
```

You can also deploy and verify in a single step using `forge create`:

```bash theme={"dark"}
forge create src/Counter.sol:Counter \
  --rpc-url https://evm-rpc-testnet.sei-apis.com \
  --private-key $PRIVATE_KEY \
  --verify \
  --verifier sourcify \
  --chain-id 1328
```

<Info>If you need to pass constructor arguments, append them after the contract path. For example: `forge create src/Token.sol:Token --constructor-args "MyToken" "MTK" 18`</Info>

### Verify with Hardhat

The [`hardhat-verify`](https://hardhat.org/docs/learn-more/smart-contract-verification) plugin supports Sourcify out of the box with no additional configuration or API key.

#### 1. Install dependencies

```bash theme={"dark"}
npm install --save-dev hardhat @nomicfoundation/hardhat-verify
```

#### 2. Configure `hardhat.config.ts`

```ts theme={"dark"}
import { defineConfig, configVariable } from "hardhat/config";
import hardhatVerify from "@nomicfoundation/hardhat-verify";

export default defineConfig({
  networks: {
    sei_testnet: {
      type: "http",
      chainId: 1328,
      url: "https://evm-rpc-testnet.sei-apis.com",
      accounts: [configVariable("SEI_PRIVATE_KEY")],
    },
    sei_mainnet: {
      type: "http",
      chainId: 1329,
      url: "https://evm-rpc.sei-apis.com",
      accounts: [configVariable("SEI_PRIVATE_KEY")],
    },
  },

  solidity: {
    compilers: [
      {
        version: "0.8.28",
        settings: {
          optimizer: { enabled: true, runs: 200 },
        },
      },
    ],
  },

  plugins: [hardhatVerify],
});
```

<Info>Sourcify verification is enabled by default in `hardhat-verify`. No `verify` config block is needed for Sourcify.</Info>

#### 3. Deploy

```bash theme={"dark"}
npx hardhat ignition deploy ignition/modules/Counter.ts --network sei_testnet
```

#### 4. Verify with Sourcify

```bash theme={"dark"}
npx hardhat verify sourcify --network sei_testnet <CONTRACT_ADDRESS>
```

If your contract has constructor arguments:

```bash theme={"dark"}
npx hardhat verify sourcify --network sei_testnet <CONTRACT_ADDRESS> "arg1" "arg2"
```

Running `npx hardhat verify` without a subtask will attempt verification on all enabled providers (Etherscan, Blockscout, and Sourcify) simultaneously.

### Verify via Sourcify UI

You can also verify contracts directly through the [Sourcify web interface](https://verify.sourcify.dev/):

1. Go to [verify.sourcify.dev](https://verify.sourcify.dev/)
2. Select **Sei** (chain ID 1329) or **Sei Testnet** (chain ID 1328)
3. Enter your contract address
4. Upload your source files or standard JSON input
5. Click **Verify**

## Remix Contract Verification Plugin

Remix IDE offers an automated contract verification solution through its Contract Verification plugin, which integrates with both Sourcify and Etherscan verification services.

Don't have a contract deployed yet? Spin up Remix with a sample `Counter.sol`, deploy it to Sei testnet, then follow the steps below to verify it.

<AddSeiButton network="testnet" label="Add Sei testnet" />

<SandboxEmbed kind="remix" src="https://remix.ethereum.org/?#activate=solidity,fileManager&code=Ly8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVApwcmFnbWEgc29saWRpdHkgXjAuOC4yNDsKCi8vLyBAdGl0bGUgQ291bnRlciDigJQgbWluaW1hbCBjb250cmFjdCB0byBjb21waWxlICYgZGVwbG95IHRvIFNlaSB0ZXN0bmV0Lgpjb250cmFjdCBDb3VudGVyIHsKICAgIHVpbnQyNTYgcHVibGljIGNvdW50OwogICAgZnVuY3Rpb24gaW5jcmVtZW50KCkgZXh0ZXJuYWwgeyBjb3VudCsrOyB9CiAgICBmdW5jdGlvbiBkZWNyZW1lbnQoKSBleHRlcm5hbCB7IGNvdW50LS07IH0KfQ" title="Counter.sol · deploy to Sei testnet" description="Remix IDE with a minimal Counter contract preloaded. Deploy it to Sei testnet, then verify it with the Contract Verification plugin below." />

### Setup and Configuration

1. **Activate the Plugin**: In Remix IDE, navigate to the Plugin Manager and activate the **Contract Verification** plugin.

2. **Configure Network Settings**:
   * Search for and select **Sei** as your blockchain
   * Choose your deployment environment:
     * **Testnet** (1328) for development
     * **Mainnet** (1329) for production

3. **Enable Verification Services**: Activate both verification methods:
   * ✅ **Sourcify**
   * ✅ **Etherscan**

4. **Etherscan Configuration**: Configure the following settings:
   * **API Key**: Obtain from [seiscan.io](https://seiscan.io)
   * **URL**: `https://seiscan.io`

### Deploy and Verify

Once configured, deploy your contract through Remix as usual. The Contract Verification plugin will automatically verify your contract upon successful deployment, submitting to both Sourcify and Seiscan simultaneously.

Verification status will be displayed in the plugin interface, and your verified contract will be publicly viewable on Seiscan explorer.

## Verify via 0xngmi Etherscan Verification (third‑party)

You can batch‑verify a contract that is already verified on another chain using the community tool [Etherscan Verification by 0xngmi](https://etherscan-verification.0xngmi.com/). This copies the verified source from a "source chain" and submits it to [Seiscan](https://seiscan.io), as long as the bytecode and compiler settings match.

<Warning>Prerequisite: The contract is already verified on at least one supported chain (same compiler version, optimization runs, constructor args, and library addresses as on Sei).</Warning>

Steps:

1. Open [Etherscan Verification by 0xngmi](https://etherscan-verification.0xngmi.com/).
2. In "Source Contract", paste the address of the contract that is already verified and select its chain.
3. In "Target Contracts", add your Sei deployment address and select "Sei" as the target chain.
4. Click "Verify Contracts" and wait for the result.
5. Confirm the result on [Seiscan](https://seiscan.io) by visiting the contract page and checking the Contract tab.

<Info>If verification fails with a bytecode mismatch, re‑check compiler version, optimization runs, constructor arguments, and linked library addresses; they must be identical to the deployment on Sei.</Info>
