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

# Contracts 3.0

> World ID 3.0 smart contracts overview: supported chains, architecture, World ID Router verifyProof, and sybil resistance.

<Warning>
  This page documents World ID 3.0 (legacy) smart contracts. For the latest on-chain verification guide including World ID 4.0, see the [On-chain Verification page](/world-id/idkit/onchain-verification) and the [World ID 4.0 Migration guide](/world-id/4-0-migration).
</Warning>

All of our smart contracts are available on GitHub:

* [World ID Smart Contracts](https://github.com/worldcoin/world-id-contracts)
* [State Bridge Smart Contracts](https://github.com/worldcoin/world-id-state-bridge)

<Note>
  If you're interested in using World ID and verifying proofs on-chain, see our
  [On-Chain Verification guide](/world-id/idkit/onchain-verification).
</Note>

## Supported Chains

<table>
  <thead>
    <tr>
      <th className="p-2 text-left align-middle">Chain</th>
      <th className="p-2 text-left align-middle">Testnet</th>
      <th className="p-2 text-left align-middle">Role</th>
      <th className="p-2 text-left align-middle">Identity Availability</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td className="p-2 align-middle">
        <img src="https://mintcdn.com/tfh/v0PtNd2L1YxZc3lg/logo/world-logo-no-text.svg?fit=max&auto=format&n=v0PtNd2L1YxZc3lg&q=85&s=9b1e09b32c698bd1c7881861869e6ef0" alt="World Chain" className="inline-block w-7 h-7 mr-2 align-middle" width="1024" height="1024" data-path="logo/world-logo-no-text.svg" />

        <strong>World Chain</strong>
      </td>

      <td className="p-2 align-middle">World Chain</td>
      <td className="p-2 align-middle">Bridged</td>
      <td className="p-2 align-middle">\~5 Minutes after Ethereum</td>
    </tr>

    <tr>
      <td className="p-2 align-middle">
        <img src="https://mintcdn.com/tfh/AMqICHmtFfYZQ_44/icons/ethereum.svg?fit=max&auto=format&n=AMqICHmtFfYZQ_44&q=85&s=667217ec93f5e446260f9cb9bc5bc6f1" alt="Ethereum" className="inline-block w-5 h-5 mr-2 align-middle" width="256" height="256" data-path="icons/ethereum.svg" />

        <strong>Ethereum</strong>
      </td>

      <td className="p-2 align-middle">Sepolia</td>
      <td className="p-2 align-middle">Canonical</td>
      <td className="p-2 align-middle">\~60 minutes after verification</td>
    </tr>

    <tr>
      <td className="p-2 align-middle">
        <img src="https://mintcdn.com/tfh/AMqICHmtFfYZQ_44/icons/optimism.svg?fit=max&auto=format&n=AMqICHmtFfYZQ_44&q=85&s=a5d8834bee4ee14b52b7f4759df961b5" alt="Optimism" className="inline-block w-5 h-5 mr-2 align-middle" width="256" height="256" data-path="icons/optimism.svg" />

        <strong>Optimism</strong>
      </td>

      <td className="p-2 align-middle">Optimism Sepolia</td>
      <td className="p-2 align-middle">Bridged</td>
      <td className="p-2 align-middle">\~5 Minutes after Ethereum</td>
    </tr>

    <tr>
      <td className="p-2 align-middle">
        <img src="https://mintcdn.com/tfh/AMqICHmtFfYZQ_44/icons/polygon.svg?fit=max&auto=format&n=AMqICHmtFfYZQ_44&q=85&s=2a0ead8ba0d0fc31d4896e480b89b079" alt="Polygon" className="inline-block w-5 h-5 mr-2 align-middle" width="256" height="256" data-path="icons/polygon.svg" />

        <strong>Polygon</strong>
      </td>

      <td className="p-2 align-middle">Polygon</td>
      <td className="p-2 align-middle">Bridged</td>
      <td className="p-2 align-middle">\~40 Minutes after Ethereum</td>
    </tr>

    <tr>
      <td className="p-2 align-middle">
        <img src="https://mintcdn.com/tfh/AMqICHmtFfYZQ_44/icons/base.svg?fit=max&auto=format&n=AMqICHmtFfYZQ_44&q=85&s=1dc4ab2f4757c6b291785b64face226b" alt="Base" className="inline-block w-5 h-5 mr-2 align-middle" width="111" height="111" data-path="icons/base.svg" />

        <strong>Base</strong>
      </td>

      <td className="p-2 align-middle">Base</td>
      <td className="p-2 align-middle">Bridged</td>
      <td className="p-2 align-middle">\~5 Minutes after Ethereum</td>
    </tr>
  </tbody>
</table>

<Note>
  Find our smart contract addresses in the
  [On-chain Verification guide](/world-id/idkit/onchain-verification).
</Note>

## Architecture

This section offers a high-level overview of the various smart contracts that make up World ID. This structure (including state bridging) is replicated on testnets -- currently Sepolia, Optimism Sepolia, and Base Sepolia.

### Identity Managers: `WorldIdIdentityManager`

Identity Managers are only deployed on Ethereum. The Identity Manager contracts are responsible for managing the Semaphore instance. Worldcoin's signup sequencers call the Identity Manager contracts to add or remove identities from the merkle tree.

### State Bridges: `OpStateBridge`/`PolygonStateBridge`

One State Bridge contract is deployed on Ethereum for each bridged chain. It publishes the root of the merkle tree to its configured chain's World ID contract, allowing proofs to be verified on that chain.

### Bridged World ID: `OpWorldId`/`PolygonWorldId`

One World ID contract is deployed on each bridged chain, with an associated State Bridge contract on Ethereum. It is responsible for receiving merkle roots from its State Bridge contract, and verifying World ID proofs against those roots.

<Note>
  You can deploy your own State Bridge contract on Ethereum and Bridged World ID
  contract to any chain to bridge World ID to that chain permissionlessly.
</Note>

### World ID Router: `WorldIdRouter`

**This is the contract you should interact with.**

The World ID Router will route your call to the correct Identity Manager contract (Ethereum) or World ID contract (L2 Chains) based on the `groupId` argument. This contract is proxied, so you will not need to update your code if the underlying contracts are upgraded.

<Note>
  Only Orb credentials are supported on-chain, so the `groupId` must be `1`.
</Note>

## Usage

The `verifyProof` method of the **World ID Router** is used to verify proofs on-chain.

### Arguments

<table>
  <thead>
    <tr>
      <th className="p-2 text-left align-middle">Parameter</th>
      <th className="p-2 text-left align-middle">Type</th>
      <th className="p-2 text-left align-middle">Description</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td className="p-2 align-middle">
        <code>root</code>
      </td>

      <td className="p-2 align-middle">
        <code>uint256</code>
      </td>

      <td className="p-2 align-middle">The World ID root to verify against.</td>
    </tr>

    <tr>
      <td className="p-2 align-middle">
        <code>groupId</code>
      </td>

      <td className="p-2 align-middle">
        <code>uint256</code>
      </td>

      <td className="p-2 align-middle">
        Determines which Credential Type to verify against. As only Orb
        credentials are supported on-chain, this must be <code>1</code>.
      </td>
    </tr>

    <tr>
      <td className="p-2 align-middle">
        <code>signalHash</code>
      </td>

      <td className="p-2 align-middle">
        <code>uint256</code>
      </td>

      <td className="p-2 align-middle">
        The <code>keccak256</code> hash of the signal to verify.
      </td>
    </tr>

    <tr>
      <td className="p-2 align-middle">
        <code>nullifierHash</code>
      </td>

      <td className="p-2 align-middle">
        <code>uint256</code>
      </td>

      <td className="p-2 align-middle">
        The root of the merkle tree to verify against. This is obtained from the
        IDKit widget as a hex string <code>nullifier\_hash</code>, and must be
        converted to a <code>uint256</code> before passing it to the{" "}
        <code>verifyProof</code> method.
      </td>
    </tr>

    <tr>
      <td className="p-2 align-middle">
        <code>externalNullifierHash</code>
      </td>

      <td className="p-2 align-middle">
        <code>uint256</code>
      </td>

      <td className="p-2 align-middle">
        The <code>keccak256</code> hash of the <code>externalNullifier</code> to
        verify. The <code>externalNullifier</code> is computed from the{" "}
        <code>app\_id</code> and <code>action</code>.
      </td>
    </tr>

    <tr>
      <td className="p-2 align-middle">
        <code>proof</code>
      </td>

      <td className="p-2 align-middle">
        <code>uint256\[8]</code>
      </td>

      <td className="p-2 align-middle">
        The zero-knowledge proof to verify. This is obtained from the IDKit
        widget as a hex string <code>proof</code>, and must be converted to a{" "}
        <code>uint256\[8]</code> before passing it to the{" "}
        <code>verifyProof</code> method.
      </td>
    </tr>
  </tbody>
</table>

#### Example: groupId

```solidity title="Orb-Only groupId" theme={null}
uint256 internal immutable groupId = 1;
```

#### Example: signalHash

```solidity title="signalHash" theme={null}
abi.encodePacked(signal).hashToField();
```

#### Example: externalNullifierHash

```solidity title="externalNullifierHash" theme={null}
externalNullifier = abi.encodePacked(abi.encodePacked(appId).hashToField(), action)
externalNullifierHash = externalNullifier.hashToField();
```

<Note>Read more about the External Nullifier in Protocol Internals.</Note>

#### Example: Unpacking Proof

<CodeGroup>
  ```ts title="viem" theme={null}
  import { decodeAbiParameters } from 'viem'

  const unpackedProof = decodeAbiParameters([{ type: 'uint256[8]' }], proof)[0]

  ```

  ```ts title="ethers.js" theme={null}
  import { defaultAbiCoder as abi } from '@ethers/utils'

  const unpackedProof = abi.decode(['uint256[8]'], proof)[0]
  ```
</CodeGroup>

### Sybil resistance

While the World ID protocol makes it very easy to make your contracts sybil resistant, this takes a little more than just calling the `verifyProof` function. To make your contract sybil-resistant, you'll need to do the following:

* Store the `nullifier` of each user that has successfully verified a proof.
* When a user attempts to verify a proof, check that the `nullifier` is not already in the list of used `nullifier`s.

Here's an example function doing the above. You can also use the [World ID starter kits](/world-id/idkit/onchain-verification) to get started with sybil resistance.

```solidity theme={null}
/// @param root The root (returned by the IDKit widget).
/// @param groupId The group ID
/// @param signal An arbitrary input from the user, usually the user's wallet address
/// @param nullifier The nullifier for this proof, preventing double signaling (returned by the IDKit widget).
/// @param proof The zero-knowledge proof that demonstrates the claimer is registered with World ID (returned by the IDKit widget).
function verifyAndExecute(
    address signal,
    uint256 root,
    uint256 nullifier,
    uint256[8] calldata proof
) public {
    // First make sure this person hasn't done this before
    if (nullifiers[nullifier]) revert InvalidNullifier();

    // Verify the provided proof is valid and the user is verified by World ID
    worldId.verifyProof(
        root,
        groupId,
        abi.encodePacked(signal).hashToField(),
        nullifier,
        externalNullifierHash,
        proof
    );

    // Record the user has done this, so they can't do it again (sybil-resistance)
    nullifiers[nullifier] = true;

    // Finally, execute your logic here, for example issue a token, NFT, etc...
}
```
