Skip to main content

WorldCoin State Replication smart contracts reference


It's a modified identity contract meant for the destination chains. It checks a TSS from the Rarimo validators to transit the state and only holds a partial history. The relayer service lazily updates the contract whenever a user wants to submit ZKP on a particular chain.


contract IdentityManager is IIdentityManager, Signers {

uint256 public constant ROOT_EXPIRATION_TIME = 1 hours;

address public sourceStateContract;

uint256 internal _latestRoot;

uint256 internal _latestTimestamp;

mapping(uint256 => RootData) internal _roots;

function signedTransitRoot(
uint256 prevRoot_,
uint256 postRoot_,
uint256 replacedAt_,
bytes calldata proof_
) external override {
// ...

emit SignedRootTransited(prevRoot_, postRoot_, replacedAt_, _latestRoot);
  • ROOT_EXPIRATION_TIME — a fixed time after which the root is considered "expired", i.e., proofs with it are considered invalid after that;
  • sourceStateContract — address of the state contract on the source chain (Rarimo);
  • _latestRoot — latest (and newest) transited root;
  • _latestTimestamp — timestamp of latest root replacement;
  • _roots — mapping of roots to data about these roots (replacement timestamp and root that replaced this one). It is updated after the transition is done;
  • signedTransitRoot(...) — the main function that adds the information to the history (_roots). It changes the _latestRoot if postRoot_ is newer. Requires the threshold signature of Rarimo signature providers. Also, emits the SignedRootTransited event, which contains information about the transition (both roots, timestamp of replacement, and current latest root);

For the full implementation, see IdentityManager.sol at the GitHub.


It's an implementation of a verifier contract. You can add your business logic to it by modifying (or overriding) two hooks – _beforeProofSubmit and _afterProofSubmit;


contract Verifier is IVerifier, Initializable {

address public semaphoreVerifier;
address public identityManager;

function verifyProof(
uint256 root_,
uint256 signalHash_,
uint256 nullifierHash_,
uint256 externalNullifierHash_,
uint256[8] calldata proof_
) public virtual override {
// ...


[root_, nullifierHash_, signalHash_, externalNullifierHash_]


function _beforeProofValidation() internal virtual {}

function _afterProofValidation() internal virtual {}

  • semaphoreVerifier — address of the SemaphoreVerifier contract that verifies the proof;
  • identityManager — address of the IdentityManager contract;
  • verifyProof(...) — the main function that verifies the user's proof. It calls the IdentityManager to verify whether the root is actual and SemaphoreVerifier to verify the proof itself;
  • _beforeProofValidation() — hook that should be executed before the verifyProof(...);
  • _afterProofValidation() — hook that gets executed after the verifyProof(...) call;

For the full implementation, see Verifier.sol at the GitHub.


This contract is responsible for verifying the ZKP. It has some necessary math functions, constants, and the primary verifyProof(...) function. It allows proofs to be verified in uncompressed or compressed form. Usually, uncompressed form is used, see BN254 Point Compression for further explanation.


contract SemaphoreVerifier is ISemaphoreVerifier {

/* ... */

function verifyCompressedProof(
uint256[4] calldata compressedProof,
uint256[4] calldata input
) public view {
// ...

function verifyProof(uint256[8] calldata proof, uint256[4] calldata input) public view {
// ...
  • verifyCompressedProof(...) — verifies a compressed proof;
  • verifyProof(...) — verifies an uncompressed proof;

For the full implementation, see SemaphoreVerifier.sol at the GitHub.