> ## 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.

# Deploy and Verify

> Deploying and verifying smart contracts on Sei with viem, ethers, Foundry, and Hardhat

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>;
};

Sei is EVM-compatible — standard deployment tooling works without modification. This page covers deploying a contract and verifying its source on the Sei block explorer.

For a deeper look at verification options (Remix, Sourcify UI, batch verification), see the [Verify Contracts](/evm/evm-verify-contracts) page.

## Try it: deploy from your browser

No local toolchain required — compile and deploy a contract to Sei testnet straight from the browser. The Remix sandbox below preloads a minimal `Counter.sol`.

First, add Sei testnet to your wallet:

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

Then in Remix: compile under the **Solidity Compiler** tab, open **Deploy & Run**, set **Environment** to **Injected Provider — MetaMask**, and click **Deploy**. Sei has instant finality, so the deployment confirms in well under a second.

<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. Compile in-browser, then deploy to Sei testnet." />

## Deploying with viem or ethers

<CodeGroup>
  ```ts viem theme={"dark"}
  import { createPublicClient, createWalletClient, http } from 'viem';
  import { privateKeyToAccount } from 'viem/accounts';
  import { sei } from 'viem/chains';

  const account = privateKeyToAccount('0xYourPrivateKey');
  const publicClient = createPublicClient({ chain: sei, transport: http() });
  const walletClient = createWalletClient({ account, chain: sei, transport: http() });

  const hash = await walletClient.deployContract({
    abi: CONTRACT_ABI,
    bytecode: '0x608060...',
    args: [/* constructor args */],
  });

  const { contractAddress } = await publicClient.waitForTransactionReceipt({ hash });
  console.log('Deployed at:', contractAddress);
  ```

  ```ts ethers theme={"dark"}
  import { ethers } from 'ethers';

  const provider = new ethers.JsonRpcProvider('https://evm-rpc.sei-apis.com');
  const wallet = new ethers.Wallet('0xYourPrivateKey', provider);

  const factory = new ethers.ContractFactory(CONTRACT_ABI, '0x608060...', wallet);
  const contract = await factory.deploy(/* constructor args */);

  await contract.waitForDeployment();
  console.log('Deployed at:', await contract.getAddress());
  ```
</CodeGroup>

## Deploying with Foundry

Foundry works against Sei with the standard `--rpc-url` flag.

```bash theme={"dark"}
forge create src/MyContract.sol:MyContract \
  --rpc-url https://evm-rpc.sei-apis.com \
  --private-key $PRIVATE_KEY \
  --constructor-args arg1 arg2
```

For testnet:

```bash theme={"dark"}
forge create src/MyContract.sol:MyContract \
  --rpc-url https://evm-rpc-testnet.sei-apis.com \
  --private-key $PRIVATE_KEY
```

## Deploying with Hardhat

Add Sei networks to your `hardhat.config.ts` (Hardhat 3). Keep the toolbox in the `plugins` array — it ships with projects created by `npx hardhat --init` and provides Hardhat Ignition for deployments:

```ts theme={"dark"}
import { defineConfig, configVariable } from 'hardhat/config';
import hardhatToolboxMochaEthers from '@nomicfoundation/hardhat-toolbox-mocha-ethers';

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

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

Store your private key in Hardhat's encrypted keystore so it never lives in a plaintext file:

```bash theme={"dark"}
npx hardhat keystore set SEI_PRIVATE_KEY
```

Describe the deployment in a minimal Ignition module at `ignition/modules/MyContract.ts`:

```ts theme={"dark"}
import { buildModule } from '@nomicfoundation/hardhat-ignition/modules';

export default buildModule('MyContractModule', (m) => {
  const myContract = m.contract('MyContract', [/* constructor args */]);
  return { myContract };
});
```

Then deploy it:

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

## Verifying Contracts

Sei's block explorer is [Seiscan](https://seiscan.io). The recommended verification method is [Sourcify](https://sourcify.dev/) — no API key required.

### Foundry

Verify after deployment:

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

Or deploy and verify in one step:

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

For testnet, use `--chain-id 1328` and `--rpc-url https://evm-rpc-testnet.sei-apis.com`.

### Hardhat

If you use a Hardhat toolbox (as in the config above), `hardhat-verify` is already bundled and active — skip ahead to the verify command. Without a toolbox, install the plugin:

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

Then add it to the `plugins` array in `hardhat.config.ts` — Sourcify verification is enabled by default in `hardhat-verify` and needs no API key:

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

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

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

Verify a deployed contract on Sourcify:

```bash theme={"dark"}
npx hardhat verify sourcify --network sei 0xDeployedContractAddress
```

If your contract has constructor arguments:

```bash theme={"dark"}
npx hardhat verify sourcify --network sei 0xDeployedContractAddress "arg1" "arg2"
```

Running `npx hardhat verify` without the `sourcify` subtask attempts verification on all enabled providers (Etherscan, Blockscout, and Sourcify) in a single run.

## Network Reference

| Network | Chain ID | RPC                                     | Explorer                                         |
| ------- | -------- | --------------------------------------- | ------------------------------------------------ |
| Mainnet | 1329     | `https://evm-rpc.sei-apis.com`          | [seiscan.io](https://seiscan.io)                 |
| Testnet | 1328     | `https://evm-rpc-testnet.sei-apis.com`  | [testnet.seiscan.io](https://testnet.seiscan.io) |
| Devnet  | 713715   | `https://evm-rpc-arctic-1.sei-apis.com` | —                                                |
