# Proof of Humanity 2.0 Integration Guide

### Key Features

* **Soulbound IDs**: Persistent human identities that survive wallet changes
* **Multi-chain deployment**: Available on Ethereum mainnet and Gnosis chain
* **Cross-chain transfers**: Move identities between supported chains
* **Enhanced vouching**: Support for both on-chain and off-chain (EIP-712) vouching
* **V1 compatibility**: Seamless migration path from V1

### Core Concepts

#### Soulbound IDs

Proof of Humanity V2 introduces **soulbound IDs** that represent unique human identities. Unlike V1 where each registration corresponded to a specific wallet address, V2 uses unique humanity IDs (`pohID` or `humanityId`) that persist across wallet changes.

**Key principle:** `1 human → 1 wallet address ↔ 1 humanity`&#x20;

If a user loses access to their wallet, they can request removal of the lost address and register with a new one using the same PoH ID, preserving their reputation and assets tied to the identity.

**Recovery Process:**

```solidity
// 1. Human loses access to wallet
humanity.owner == lostAddress
isHuman(lostAddress) == true

// 2. Human requests removal of lost address
humanity.owner == address(0)
isHuman(lostAddress) == false

// 3. Human registers with new wallet address
humanity.owner == newAddress
isHuman(newAddress) == true
```

#### Multi-Chain Architecture

PoH V2 operates across multiple blockchains with a sophisticated state synchronization system:

* **Home Chain**: The primary chain where a humanity is claimed and managed
* **State Updates**: Humanity status synchronized across chains via bridge gateways
* **Universal IDs**: Humanity IDs are consistent across all supported chains
* **Transfer Mechanism**: Humanities can be transferred between chains with cooldown periods

{% hint style="info" %}
**Important**: A humanity can only have **one active home chain** at a time, but its state can be reflected on multiple chains.
{% endhint %}

#### Humanity Lifecycle

A humanity progresses through distinct states: \
**Unclaimed** → **Vouching** → **Resolving** → **Claimed** → **Renewal** → **Expired/Revoked**

### Contract Architecture

#### ProofOfHumanity Contract

* Core functionality similar to V1 with enhanced soulbound features
* Manages humanity claims, renewals, and revocations
* Handles vouching and challenge mechanisms with dispute resolution
* Supports both on-chain and off-chain (EIP-712) vouching

#### ProofOfHumanityExtended Contract

* Enhanced version deployed on Ethereum mainnet
* Includes **Fork Module** for V1 compatibility
* Allows seamless transition from V1 to V2

#### CrossChainProofOfHumanity Contract

* Manages cross-chain state updates and transfers
* Stores received state updates from other chains
* Handles humanity transfers between chains with transfer cooldowns
* Ensures consistency across multi-chain deployments

#### Bridge Gateways

* **AMB (Arbitrary Message Bridge)** gateways for cross-chain communication
* Enable secure message passing between chain instances
* Support both state updates and humanity transfers

### Deployment Addresses

#### Ethereum Mainnet

<table><thead><tr><th width="313.34375">Contract</th><th>Address</th></tr></thead><tbody><tr><td>ProofOfHumanityExtended</td><td>0xbE9834097A4E97689d9B667441acafb456D0480A</td></tr><tr><td>CrossChainProofofHumanity</td><td>0xa478095886659168E8812154fB0DE39F103E74b2</td></tr><tr><td>AMB Bridge Gateway</td><td>0xddafACf8B4a5087Fc89950FF7155c76145376c1e</td></tr><tr><td>Fork Module</td><td>0x068a27Db9c3B8595D03be263d52c813cb2C99cCB</td></tr></tbody></table>

#### Gnosis Chain

<table><thead><tr><th width="281.46875">Contract</th><th>Address </th></tr></thead><tbody><tr><td>ProofOfHumanity</td><td>0xa4AC94C4fa65Bb352eFa30e3408e64F72aC857bc</td></tr><tr><td>CrossChainProofOfHumanity</td><td>0x16044E1063C08670f8653055A786b7CC2034d2b0</td></tr><tr><td>AMB Bridge Gateway</td><td>0x6Ef5073d79c42531352d1bF5F584a7CBd270c6B1</td></tr></tbody></table>

### Interface Definitions&#x20;

Core Interface

```solidity
interface IProofOfHumanity {
    // Core verification functions
    function isHuman(address _account) external view returns (bool);
    function isClaimed(bytes20 _humanityId) external view returns (bool);
    function humanityOf(address _account) external view returns (bytes20);
    function boundTo(bytes20 _humanityId) external view returns (address);
    
    // Detailed information function
    function getHumanityInfo(bytes20 _humanityId) external view returns (
        bool vouching,              // Is this humanity currently vouching for someone
        bool pendingRevocation,     // Is there a pending revocation request
        uint48 nbPendingRequests,   // Number of pending requests in challenging phase
        uint40 expirationTime,      // When the humanity expires
        address owner,              // Current owner address
        uint256 nbRequests          // Total number of requests made
    );
    
    // Statistics and request information
    function getClaimerRequestId(address _claimer) external view returns (uint256);
    function getNumberOfVouches(bytes20 _humanityId, uint256 _requestId) external view returns (uint256);
    function getHumanityCount() external view returns (uint256);
}
```

#### Vouching Interface

```solidity
interface IProofOfHumanityVouching {
    
    struct SignatureVouch {
        uint40 expirationTime; // Time when the signature expires
        uint8 v;               // 'v' value of the signature  
        bytes32 r;             // 'r' value of the signature
        bytes32 s;             // 's' value of the signature
    }
    
    // Basic vouching functions
    function addVouch(address _account, bytes20 _humanityId) external;
    function removeVouch(address _account, bytes20 _humanityId) external;
    function vouches(address _voucher, address _claimer, bytes20 _humanityId) external view returns (bool);
    
    // Advanced vouching with signature support
    function advanceState(
        address _claimer,
        address[] calldata _vouches,
        SignatureVouch[] calldata _signatureVouches
    ) external;
}
```

#### Cross-Chain interface

```solidity
interface ICrossChainProofOfHumanityView {
    // View functions (same as main interface)
    function isHuman(address _account) external view returns (bool);
    function isClaimed(bytes20 _humanityId) external view returns (bool);
    function boundTo(bytes20 _humanityId) external view returns (address);
    function humanityOf(address _account) external view returns (bytes20);
    
    // Cross-chain operations
    function updateHumanity(address _bridgeGateway, bytes20 _humanityId) external;
    function transferHumanity(address _bridgeGateway) external;
       
    // Receive functions (called by bridge)
    function receiveUpdate(address _owner, bytes20 _humanityId, uint40 _expirationTime, bool _isActive) external;
    function receiveTransfer(address _owner, bytes20 _humanityId, uint40 _expirationTime, bytes32 _transferHash) external;
}

```

#### Request Management Interface

```solidity
interface IProofOfHumanityRequests {
   enum Party {
        None,
        Requester,
        Challenger
    }

    enum Reason {
        None,
        IncorrectSubmission,
        IdentityTheft,
        SybilAttack,
        Deceased
    }
    
     // Request creation
    function claimHumanity(bytes20 _humanityId, string calldata _evidence, string calldata _name) external payable;
    function renewHumanity(string calldata _evidence) external payable;
    function revokeHumanity(bytes20 _humanityId, string calldata _evidence) external payable;
    
    // Request management
    function fundRequest(bytes20 _humanityId, uint256 _requestId) external payable;
    function withdrawRequest() external;
    function executeRequest(bytes20 _humanityId, uint256 _requestId) external;
    
    // Challenges and disputes
    function challengeRequest(
        bytes20 _humanityId,
        uint256 _requestId,
        Reason _reason,
        string calldata _evidence
    ) external payable;
    
    function fundAppeal(address _arbitrator, uint256 _disputeId, Party _side) external payable;
    
    // Evidence and fee management
    function submitEvidence(bytes20 _humanityId, uint256 _requestId, string calldata _evidence) external;
    function withdrawFeesAndRewards(
        address payable _beneficiary,
        bytes20 _humanityId,
        uint256 _requestId,
        uint256 _challengeId,
        uint256 _round
    ) external;
}
```

### Quick Start Integration

#### 1. Install Interface

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface IProofOfHumanity {
    function isHuman(address _account) external view returns (bool);
    function humanityOf(address _account) external view returns (bytes20);
    function isClaimed(bytes20 _humanityId) external view returns (bool);
    function boundTo(bytes20 _humanityId) external view returns (address);
}
```

#### 2. Basic Integration

```solidity
contract MyDApp {
    IProofOfHumanity public immutable poh;
    
    constructor(address _pohAddress) {
        poh = IProofOfHumanity(_pohAddress);
    }
    
    modifier onlyHuman() {
        require(poh.isHuman(msg.sender), "Must be verified human");
        _;
    }
    
    function humanOnlyFunction() external onlyHuman {
        // Your logic here
    }
}
```

#### 3. Chain Selection

Choose the appropriate contract address based on your deployment chain:

```solidity
// For Ethereum mainnet
address constant POH_ETHEREUM = 0xbE9834097A4E97689d9B667441acafb456D0480A;

// For Gnosis chain  
address constant POH_GNOSIS = 0xa4AC94C4fa65Bb352eFa30e3408e64F72aC857bc;
```

#### Method 1: Basic Human Verification

**Use case**: Simple verification that an address belongs to a verified human.

**Implementation Example**

```solidity
contract BasicVerification {
    IProofOfHumanity public immutable poh;
    
    constructor(address _pohAddress) {
        poh = IProofOfHumanity(_pohAddress);
    }
    
    function verifyHuman(address _user) external view returns (bool) {
        return poh.isHuman(_user);
    }
    
    function getHumanityInfo(address _user) external view returns (
        bool isVerified,
        bytes20 humanityId
    ) {
        humanityId = poh.humanityOf(_user);
        isVerified = humanityId != bytes20(0);
    }
}
```

#### Method 2: Soulbound Identity Integration

**Use Case**: Applications requiring persistent identity across wallet changes (reputation systems, social platforms, DAOs).

**Implementation**

```solidity
contract SoulboundIntegration {
    IProofOfHumanity public immutable poh;
    
    struct UserProfile {
        uint256 registrationTime;
        uint256 reputation;
        string username;
        bool isActive;
    }
    
    // Store data by humanity ID, not address
    mapping(bytes20 => UserProfile) public profiles;
    mapping(bytes20 => address) public currentAddress;
    
    event ProfileCreated(bytes20 indexed humanityId, address indexed user);
    event AddressUpdated(bytes20 indexed humanityId, address indexed oldAddress, address indexed newAddress);
    
    function createProfile(string calldata _username) external {
        //First verify user is actually human
        require(poh.isHuman(msg.sender), "Must be verified human");
        
        //Then get their humanity ID
        bytes20 humanityId = poh.humanityOf(msg.sender);
        require(humanityId != bytes20(0), "Failed to get humanity ID");
        require(!profiles[humanityId].isActive, "Profile already exists");
        
        profiles[humanityId] = UserProfile({
            registrationTime: block.timestamp,
            reputation: 0,
            username: _username,
            isActive: true
        });
        
        currentAddress[humanityId] = msg.sender;
        emit ProfileCreated(humanityId, msg.sender);
    }
    
    function updateReputation(address _user, uint256 _newReputation) external {
        //Verify user is still human before updating
        require(poh.isHuman(_user), "User must be verified human");
        
        bytes20 humanityId = poh.humanityOf(_user);
        require(profiles[humanityId].isActive, "Profile not found");
        
        // Update address if it changed
        if (currentAddress[humanityId] != _user) {
            emit AddressUpdated(humanityId, currentAddress[humanityId], _user);
            currentAddress[humanityId] = _user;
        }
        
        profiles[humanityId].reputation = _newReputation;
    }
    
    function getProfile(address _user) external view returns (UserProfile memory) {
        bytes20 humanityId = poh.humanityOf(_user);
        return profiles[humanityId];
    }
    
    function getProfileByHumanityId(bytes20 _humanityId) external view returns (UserProfile memory) {
        return profiles[_humanityId];
    }

 
    //Additional helper functions for better integration
    function isProfileActive(address _user) external view returns (bool) {
        if (!poh.isHuman(_user)) return false;
        bytes20 humanityId = poh.humanityOf(_user);
        return profiles[humanityId].isActive;
    }
    
    function getActiveHumanityId(address _user) external view returns (bytes20) {
        require(poh.isHuman(_user), "User not verified");
        return poh.humanityOf(_user);
    }
    
    //Handle humanity expiration/revocation
    function deactivateExpiredProfile(bytes20 _humanityId) external {
        require(profiles[_humanityId].isActive, "Profile not active");
        
        // Check if humanity is still claimed
        address currentOwner = poh.boundTo(_humanityId);
        if (currentOwner == address(0)) {
            profiles[_humanityId].isActive = false;
        }
    }
}
```

#### Method 3: Cross-Chain Integration

**Use Case**: Applications operating across multiple chains.

**Multi-Chain Verification**

```solidity
interface ICrossChainProofOfHumanity {
    function isHuman(address _account) external view returns (bool);
    function isClaimed(bytes20 _humanityId) external view returns (bool);
    function humanityOf(address _account) external view returns (bytes20);
    function boundTo(bytes20 _humanityId) external view returns (address);
}

contract CrossChainDApp {
    IProofOfHumanity public immutable pohMain;
    ICrossChainProofOfHumanity public immutable pohCrossChain;
    
    constructor(address _pohMain, address _pohCrossChain) {
        pohMain = IProofOfHumanity(_pohMain);
        pohCrossChain = ICrossChainProofOfHumanity(_pohCrossChain);
    }
    
    function isVerifiedHuman(address _account) public view returns (bool) {
        // Check both main contract and cross-chain state
        return pohMain.isHuman(_account) || pohCrossChain.isHuman(_account);
    }
    
    function getUniversalHumanityId(address _account) public view returns (bytes20) {
        bytes20 humanityId = pohMain.humanityOf(_account);
        if (humanityId == bytes20(0)) {
            humanityId = pohCrossChain.humanityOf(_account);
        }
        return humanityId;
    }
    
    function getHumanityOwner(bytes20 _humanityId) public view returns (address) {
        address owner = pohMain.boundTo(_humanityId);
        if (owner == address(0)) {
            owner = pohCrossChain.boundTo(_humanityId);
        }
        return owner;
    }
}
```

### Advanced Integration Patterns

#### Event Monitoring

Monitor important PoH events for real-time updates:

```solidity
contract EventMonitor {
    IProofOfHumanity public immutable poh;
    
    mapping(bytes20 => bool) public activeHumanities;
    mapping(address => bytes20) public humanityToUser;
    
    event HumanityStatusChanged(bytes20 indexed humanityId, address indexed user, bool isActive);
    event HumanityAddressUpdated(bytes20 indexed humanityId, address indexed oldUser, address indexed newUser);
    
    constructor(address _pohAddress) {
        poh = IProofOfHumanity(_pohAddress);
    }
    
    // Call when monitoring HumanityClaimed events
    function handleHumanityClaimed(bytes20 humanityId, address user) external {
        require(poh.isClaimed(humanityId), "Humanity not claimed");
        activeHumanities[humanityId] = true;
        humanityToUser[user] = humanityId;
        emit HumanityStatusChanged(humanityId, user, true);
    }
    
    // Call when monitoring HumanityRevoked events  
    function handleHumanityRevoked(bytes20 humanityId, address user) external {
        activeHumanities[humanityId] = false;
        delete humanityToUser[user];
        emit HumanityStatusChanged(humanityId, user, false);
    }
}  
```

#### Detailed State Queries

For applications requiring comprehensive humanity information:

```solidity
interface IProofOfHumanityDetailed {
    function getHumanityInfo(bytes20 _humanityId) external view returns (
        bool vouching,              // Currently vouching for someone
        bool pendingRevocation,     // Has pending revocation request
        uint48 nbPendingRequests,   // Number of pending requests
        uint40 expirationTime,      // When humanity expires
        address owner,              // Current owner address
        uint256 nbRequests          // Total requests made
    );
}

contract DetailedIntegration {
    IProofOfHumanityDetailed public immutable poh;
    
    struct HumanityStatus {
        bool isValid;
        bool canVouch;
        bool isPending;
        uint256 timeToExpiry;
        address currentOwner;
    }
    
    constructor(address _pohAddress) {
        poh = IProofOfHumanityDetailed(_pohAddress);
    }
    
    function getHumanityStatus(bytes20 _humanityId) external view returns (HumanityStatus memory) {
        (
            bool vouching,
            bool pendingRevocation,
            uint48 nbPendingRequests,
            uint40 expirationTime,
            address owner,
        ) = poh.getHumanityInfo(_humanityId);
        
        bool isValid = owner != address(0) && block.timestamp < expirationTime;
        bool canVouch = isValid && !vouching && !pendingRevocation;
        bool isPending = pendingRevocation || nbPendingRequests > 0;
        uint256 timeToExpiry = expirationTime > block.timestamp ? 
                              expirationTime - block.timestamp : 0;
        
        return HumanityStatus({
            isValid: isValid,
            canVouch: canVouch,
            isPending: isPending,
            timeToExpiry: timeToExpiry,
            currentOwner: owner
        });
    }
}
```

#### Registration Vouching Integration

**Understanding PoH's Vouching System:**

PoH's vouching system is **exclusively for the registration process** - when new people are trying to become verified humans.&#x20;

**How Registration Vouching Works:**

1. Someone calls `claimHumanity()` to register as a human
2. Existing verified humans call `addVouch()` to support them
3. Once enough vouches are collected, the request can advance to the resolving phase
4. The person either becomes verified or gets challenged

### Migration from V1

#### V1 Compatibility

The `ProofOfHumanityExtended` contract includes automatic V1 compatibility through the Fork Module:

* ✅ V1 registrations continue to work
* ✅ No immediate migration required
* ✅ Seamless transition available
* ✅ `isHuman()` checks both V1 and V2

#### Migration Support Implementation

```solidity
contract MigrationHelper {
    IProofOfHumanity public immutable pohV2;
    
    constructor(address _pohV2Address) {
        pohV2 = IProofOfHumanity(_pohV2Address);
    }
    
    function getRegistrationVersion(address _user) external view returns (string memory) {
        if (!pohV2.isHuman(_user)) {
            return "not_registered";
        }
        
        bytes20 humanityId = pohV2.humanityOf(_user);
        
        // V1 users have humanity ID equal to their address
        if (humanityId == bytes20(_user)) {
            return "v1";
        }
        
        return "v2";
    }
    
    function shouldMigrate(address _user) external view returns (bool) {
        if (!pohV2.isHuman(_user)) return false;
        
        bytes20 humanityId = pohV2.humanityOf(_user);
        return humanityId == bytes20(_user); // V1 indicator
    }
    
    function isHumanAnyVersion(address _user) external view returns (bool) {
        // Automatically checks both V2 and V1 via Fork Module
        return pohV2.isHuman(_user);
    }
}
```

#### Migration Benefits

Migrating from V1 to V2 provides:

* **Soulbound identity**: Persistent across wallet changes
* **Cross-chain support**: Use identity on multiple chains
* **Enhanced security**: Improved dispute and vouching mechanisms
* **Future compatibility**: Access to new features and integrations

### Quick Reference

#### Essential Functions

```solidity
// Basic verification
function isHuman(address _account) external view returns (bool);
function humanityOf(address _account) external view returns (bytes20);

// Humanity info
function isClaimed(bytes20 _humanityId) external view returns (bool);
function boundTo(bytes20 _humanityId) external view returns (address);

// Detailed info (if needed)
function getHumanityInfo(bytes20 _humanityId) external view returns (
    bool vouching, bool pendingRevocation, uint48 nbPendingRequests,
    uint40 expirationTime, address owner, uint256 nbRequests
);
```

Contract Addresses Quick Copy

```solidity
// Ethereum Mainnet
address constant POH_ETHEREUM = 0xbE9834097A4E97689d9B667441acafb456D0480A;
address constant CROSSCHAIN_ETHEREUM = 0xa478095886659168E8812154fB0DE39F103E74b2;

// Gnosis Chain  
address constant POH_GNOSIS = 0xa4AC94C4fa65Bb352eFa30e3408e64F72aC857bc;
address constant CROSSCHAIN_GNOSIS = 0x16044E1063C08670f8653055A786b7CC2034d2b0;
```

### Support Resources

* **Documentation**: [Proof of Humanity Docs](https://proofofhumanity.id/)
* **GitHub**: [PoH V2 Repository](https://github.com/Proof-Of-Humanity/proof-of-humanity-v2-contracts)
* **Support:** <contact@kleros.io>
* **Forum**: [PoH Governance](https://gov.proofofhumanity.id/)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.kleros.io/products/proof-of-humanity/proof-of-humanity-2.0-integration-guide.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
