# Credentials Verification Service

The Humanity Credentials Verification Service allows your DApp to verify if users meet specific conditions, such as:

* Has the user completed KYC identity verification?
* Has the user connected social accounts like Google or Twitter?
* Does the user's net worth reach a certain tier?
* Is the user a member of a specific airline or hotel loyalty program?

**In simple terms**: Your smart contract can ask Humanity questions like "Does this user have a Google account?" or "Has this user passed KYC?", then decide whether to allow user actions based on the answer.

***

## How It Works

### 1. User clicks "Verify Identity" button

The user initiates the verification process from your DApp's frontend interface.

### 2. User signs authorization

The user signs a permission message using their wallet (e.g., MetaMask) to authorize the verification request.

### 3. Your DApp contract calls the Oracle

Your DApp contract submits the verification request to the Humanity Verification Oracle, including the user's signature.

### 4. Oracle backend checks user credentials

The Oracle's off-chain backend validates the user's humanity credentials against Humanity's verification system.

### 5. Oracle calls back your contract with the result

The Oracle creates an on-chain attestation and calls your contract's `onVerificationComplete` callback with the verification result.

### 6. Your contract executes business logic

Your contract processes the result (verified or not) and executes the appropriate action, such as granting access or distributing tokens.

***

## **Core Concepts**

<table><thead><tr><th width="307.375">Concept</th><th>Description</th></tr></thead><tbody><tr><td><strong>Credential</strong></td><td>A verifiable claim held by the user — may be a W3C Verifiable Credential (VC), a Zero-Knowledge proof, or a proof generated from another protocol</td></tr><tr><td><strong>Issuer</strong></td><td>The entity responsible for assessing user attributes/behavior and issuing credentials — this may be a KYC provider, the protocol itself, or an external partner</td></tr><tr><td><strong>Holder</strong></td><td>The user's wallet address that stores the credential/proof and presents it to the DApp when required</td></tr><tr><td><strong>Verifier Contract</strong></td><td>The smart contract provided by this service, responsible for verifying credentials</td></tr><tr><td><strong>Claim</strong></td><td>A specific verification condition (e.g., google_connected, mc_kyc)</td></tr><tr><td><strong>Category</strong></td><td>A grouping of related claims (e.g., IDENTITY, SOCIAL, FINANCE)</td></tr></tbody></table>

## Supported Verification Claims

### Social Account Verification

The simplest and most common verification - confirms users have connected social accounts:

| Claim ID             | Meaning                           |
| -------------------- | --------------------------------- |
| `github_connected`   | User has connected GitHub         |
| `google_connected`   | User has connected Google         |
| `discord_connected`  | User has connected Discord        |
| `twitter_connected`  | User has connected Twitter/X      |
| `linkedin_connected` | User has connected LinkedIn       |
| `telegram_connected` | User has connected Telegram       |
| `email_verified`     | User has verified email ownership |
| `humanity_identity`  | User has verified via Palm check  |

**Use Cases**: Community verification, sybil attack prevention, ensuring real users

### Identity & Financial Verification (Mastercard)

Verify user identity and assets through bank account data:

| Claim ID         | Meaning                                     | Category |
| ---------------- | ------------------------------------------- | -------- |
| `mc_kyc`         | User has completed KYC verification         | Identity |
| `mc_residency`   | User residency verification                 | Identity |
| `mc_net_worth`   | Net worth tier ($100 / $10K / $1M+)         | Finance  |
| `mc_investments` | Investment account verification             | Finance  |
| `mc_retirement`  | Retirement savings verification (401K, IRA) | Finance  |
| `mc_mortgage`    | Mortgage verification                       | Finance  |

**Use Cases**: DeFi lending, accredited investor verification, credit assessment

***

### Membership Verification

Verify user's airline/hotel loyalty program memberships:

#### **Airlines:**

| Claim ID                        | Airline                          |
| ------------------------------- | -------------------------------- |
| `delta_membership`              | Delta Airlines SkyMiles          |
| `emirates_membership`           | Emirates Skywards                |
| `singapore_airlines_membership` | Singapore Airlines KrisFlyer     |
| `american_airlines_membership`  | American Airlines AAdvantage     |
| `cathay_pacific_membership`     | Cathay Pacific Asia Miles        |
| `korean_air_membership`         | Korean Air SKYPASS               |
| `jetblue_membership`            | JetBlue TrueBlue                 |
| `thai_airways_membership`       | Thai Airways Royal Orchid Plus   |
| `virgin_australia_membership`   | Virgin Australia Velocity        |
| `frontier_airlines_membership`  | Frontier Airlines Frontier Miles |
| `spirit_airlines_membership`    | Spirit Airlines Free Spirit      |
| `etihad_membership`             | Etihad Airways Guest             |
| `ryanair_membership`            | Ryanair Membership               |
| `sas_membership`                | Scandinavian Airlines EuroBonus  |

#### **Hotels:**

| Claim ID                | Hotel Brand            |
| ----------------------- | ---------------------- |
| `marriott_membership`   | Marriott Bonvoy        |
| `hilton_membership`     | Hilton Honors          |
| `accor_membership`      | Accor Live Limitless   |
| `wyndham_membership`    | Wyndham Rewards        |
| `radisson_membership`   | Radisson Rewards       |
| `shangri_la_membership` | Shangri-La Circle      |
| `taj_hotels_membership` | Taj Hotels InnerCircle |

#### **Entertainment/Casinos:**

| Claim ID                  | Brand           |
| ------------------------- | --------------- |
| `caesars_membership`      | Caesars Rewards |
| `mgm_resorts_membership`  | MGM Rewards     |
| `wynn_resorts_membership` | Wynn Rewards    |

**Use Cases**: VIP member-exclusive events, loyalty rewards, identity verification

***

#### Crypto Exchange Verification (CEX)

| Claim ID          | Exchange | Verification Content              |
| ----------------- | -------- | --------------------------------- |
| `binance_finance` | Binance  | Account and balances verification |
| `okx_finance`     | OKX      | Account and total valuation       |

**Use Cases**: Airdrop eligibility, trading volume proof

***

## **Contract Addresses**

### **Mainnet (Humanity Chain)**

| Contract                       | Address                                      | Chain ID  |
| ------------------------------ | -------------------------------------------- | --------- |
| **HumanityVerificationOracle** | `0x8D71D8bD47860bd0381b272AE42162c3692c4F3a` | `6985385` |
| **FeeEscrow**                  | `0xe433f01131eAbD8060a1E34149eF0e79b2b86fEc` | `6985385` |

### **Testnet (Humanity Testnet)**

| Contract                       | Address                                      | Chain ID  |
| ------------------------------ | -------------------------------------------- | --------- |
| **HumanityVerificationOracle** | `0x67c0A5cA2Fb19E8E0Ff008d727aff5f128b00E09` | `7080969` |
| **FeeEscrow**                  | `0x1a247b7d7076e4c4D97D87c62947Ab5495C13423` | `7080969` |

## Quick Start

### Step 1: Set Up Your Contract

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

import { IHumanityVerificationCallback, IHumanityVerificationOracle }
    from "./IVerificationOracle.sol";

contract MyDApp is IHumanityVerificationCallback {

    // Oracle contract address (Testnet / Mainnet)
    IHumanityVerificationOracle public oracle;

    // Claims you need to verify
    string[] public REQUIRED_CLAIMS = ["google_connected"];

    // Verification validity period (30 days)
    uint256 public constant MAX_AGE = 30 days;

    // Track which users are verified
    mapping(address => bool) public verifiedUsers;

    // Track pending verification requests
    mapping(bytes32 => address) public pendingRequests;
}
```

### Step 2: Implement Verification Request

```solidity
// Users call this function to verify their identity
function verifyMe(bytes memory signature) external {
    // First check if already verified
    (bool alreadyVerified, , ) = oracle.isUserVerified(
        msg.sender,
        REQUIRED_CLAIMS,
        MAX_AGE
    );

    if (alreadyVerified) {
        verifiedUsers[msg.sender] = true;
        return; // Already verified, return early
    }

    // Request new verification
    bytes32 requestId = oracle.requestVerification(
        address(this),     // Your DApp address
        msg.sender,        // User to verify
        REQUIRED_CLAIMS,   // Verification conditions
        MAX_AGE,           // Validity period
        address(this),     // Callback address (receives result)
        signature          // User's signature authorization
    );

    pendingRequests[requestId] = msg.sender;
}
```

### Step 3: Receive Verification Result

```solidity
// Oracle calls this function when verification is complete
function onVerificationComplete(
    bytes32 requestId,
    address user,
    bool verified,      // true = verification passed
    bytes32 attestationUID
) external {
    // Security check: only Oracle can call
    require(msg.sender == address(oracle), "Only Oracle");
    require(pendingRequests[requestId] == user, "Unknown request");

    delete pendingRequests[requestId];

    if (verified) {
        // ✅ Verification passed! User meets all conditions
        verifiedUsers[user] = true;
        // Execute your business logic...
    } else {
        // ❌ Verification failed
        // Handle failure case...
    }
}
```

### Step 4: Get User Signature on Frontend

```tsx
// Frontend code (TypeScript)
import { ethers } from 'ethers';

async function getVerificationSignature(
    userAddress: string,
    dappAddress: string,
    oracleContract: ethers.Contract,
    signer: ethers.Signer
) {
    // 1. Get the message to sign from Oracle
    const messageHash = await oracleContract.getPermissionMessageHash(
        userAddress,
        dappAddress,
        ["google_connected"],  // Your verification conditions
        30 * 24 * 60 * 60,     // 30 days (in seconds)
        dappAddress
    );

    // 2. Have user sign (MetaMask will pop up)
    const signature = await signer.signMessage(ethers.getBytes(messageHash));

    return signature;
}

// Usage example
const signature = await getVerificationSignature(
    userWallet.address,
    "0xYourDAppContractAddress",
    oracleContract,
    signer
);

// Call the contract
await myDappContract.verifyMe(signature);
```

## Fee Management

### Prepaid Model Overview

Humanity Protocol uses a **prepaid model**:

> Your DApp deposits $tHP into FeeEscrow once. Verification fees are deducted automatically. Users never pay fees directly.

```
DApp deposits $tHP → Users request verification → Fees auto-deducted → Result delivered
```

***

### Quick Start

#### Deposit Funds

```solidity
IFeeEscrow feeEscrow = IFeeEscrow(0xe433f01131eAbD8060a1E34149eF0e79b2b86fEc);

// Deposit 10 $tHP for your DApp
feeEscrow.deposit{value: 10 ether}(address(yourDApp));
```

#### Check Balance

```solidity
// Get available balance
uint256 available = feeEscrow.getAvailable(address(this));

// Get full balance info
(uint256 total, uint256 reserved, uint256 available) =
    feeEscrow.getBalanceInfo(address(this));

// Check if can afford verification
(bool canAfford, ) = feeEscrow.canAffordFee(address(this), feeAmount);
```

#### Withdraw Unused Funds

```solidity
// Request withdrawal (requires admin approval)
uint256 requestId = feeEscrow.requestWithdrawal(recipientAddress, amount);

// Cancel if needed
feeEscrow.cancelWithdrawalRequest(requestId);
```

### Balance Types

| Type          | Description                      | How to Get           |
| ------------- | -------------------------------- | -------------------- |
| **Total**     | All deposited funds              | `getBalance(dapp)`   |
| **Reserved**  | Locked for pending verifications | `getReserved(dapp)`  |
| **Available** | Can use for new verifications    | `getAvailable(dapp)` |

```
Available = Total - Reserved
```

### Fee Lifecycle

```
1. RESERVE   → Fee locked when verification requested
2. PROCESS   → Oracle verifies user credentials
3. SETTLE    → Success: fee deducted | Failure: fee released back
```

**You only pay for successful verifications.**

***

### FAQ

| Question                         | Answer                                                           |
| -------------------------------- | ---------------------------------------------------------------- |
| **Who funds FeeEscrow?**         | DApp developer/operator                                          |
| **When to fund?**                | Before users start verifying                                     |
| **What if balance runs out?**    | Verification requests fail with `InsufficientAvailableBalance`   |
| **Can users steal my funds?**    | No. Only Oracle can deduct fees; withdrawals need admin approval |
| **One escrow per DApp?**         | Yes, each DApp needs its own balance                             |
| **Can I withdraw unused funds?** | Yes, request withdrawal → admin approves → funds sent            |

### Best Practice: Monitor Balance

```solidity
function checkEscrowHealth() external view returns (bool needsRefill) {
    uint256 available = feeEscrow.getAvailable(address(this));
    uint256 minRequired = verificationFee * 50; // Keep 50 verifications worth
    return available < minRequired;
}
```

***

### Fee Distribution (on successful verification)

```
25% → Credential Issuers
25% → Protocol Treasury
25% → Staking Pool
25% → Proof Generation
```

***

## Best Practices

### Check Before Requesting

```solidity
// Good practice: check if already verified first
(bool verified, , ) = oracle.isUserVerified(user, claims, maxAge);
if (verified) {
    // Use directly, no new request needed
    return;
}
// Only then request new verification
```

### Choose Appropriate Claims

```solidity
// Lending scenario: needs KYC + asset proof
string[] memory LENDING_CLAIMS = ["mc_kyc", "mc_net_worth"];

// Community scenario: needs social accounts
string[] memory COMMUNITY_CLAIMS = ["google_connected"];

// VIP members: airline/hotel membership
string[] memory VIP_CLAIMS = ["emirates_membership"];
```

### Choose Appropriate Validity Period

| Scenario                         | Recommended MAX\_AGE |
| -------------------------------- | -------------------- |
| High security (financial)        | 1-7 days             |
| Medium security (access control) | 30 days              |
| Low security (preferences)       | 90+ days             |

### Maintain Sufficient Balance

```solidity
function checkBalance() public view returns (bool needsRefill) {
    uint256 available = feeEscrow.getAvailable(address(this));
    uint256 minRequired = verificationFee * 10; // At least enough for 10 verifications
    return available < minRequired;
}
```

***

## Common Errors

| Error                         | Cause                            | Solution                |
| ----------------------------- | -------------------------------- | ----------------------- |
| `InvalidUser()`               | User address is zero             | Check user address      |
| `NoClaimsSpecified()`         | No verification claims specified | Pass at least one claim |
| `InsufficientVerificationFee` | FeeEscrow balance too low        | Deposit more ETH        |
| `UnauthorizedRequest`         | Invalid user signature           | Have user sign again    |
| `RequestNotFound`             | Request ID doesn't exist         | Check requestId         |

## Complete Example Contract

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

import { IHumanityVerificationCallback, IHumanityVerificationOracle }
    from "./interfaces/IVerificationOracle.sol";
import { IFeeEscrow } from "./interfaces/IFeeEscrow.sol";

/**
 * @title Example: Airdrop Contract Requiring Identity Verification
 * @notice Only users with verified Google accounts can claim the airdrop
 */
contract VerifiedAirdrop is IHumanityVerificationCallback {

    IHumanityVerificationOracle public immutable oracle;
    IFeeEscrow public immutable feeEscrow;

    string[] public REQUIRED_CLAIMS;
    uint256 public constant MAX_AGE = 30 days;
    uint256 public constant AIRDROP_AMOUNT = 100 ether; // 100 tokens

    mapping(bytes32 => address) public pendingClaims;
    mapping(address => bool) public hasClaimed;

    event ClaimRequested(address indexed user, bytes32 requestId);
    event AirdropClaimed(address indexed user, uint256 amount);
    event ClaimFailed(address indexed user, string reason);

    constructor(address _oracle, address _feeEscrow) {
        oracle = IHumanityVerificationOracle(_oracle);
        feeEscrow = IFeeEscrow(_feeEscrow);
        REQUIRED_CLAIMS = ["google_connected"];
    }

    /// @notice Get the message hash for user to sign
    function getClaimSignatureHash(address user) external view returns (bytes32) {
        return oracle.getPermissionMessageHash(
            user,
            address(this),
            REQUIRED_CLAIMS,
            MAX_AGE,
            address(this)
        );
    }

    /// @notice User claims airdrop
    function claimAirdrop(bytes memory signature) external {
        require(!hasClaimed[msg.sender], "Already claimed");

        // Check if already verified
        (bool verified, bytes32 attestationUID, ) = oracle.isUserVerified(
            msg.sender, REQUIRED_CLAIMS, MAX_AGE
        );

        if (verified) {
            // Already verified, distribute airdrop directly
            _sendAirdrop(msg.sender);
            return;
        }

        // Request verification
        bytes32 requestId = oracle.requestVerification(
            address(this),
            msg.sender,
            REQUIRED_CLAIMS,
            MAX_AGE,
            address(this),
            signature
        );

        pendingClaims[requestId] = msg.sender;
        emit ClaimRequested(msg.sender, requestId);
    }

    /// @notice Oracle callback
    function onVerificationComplete(
        bytes32 requestId,
        address user,
        bool verified,
        bytes32 attestationUID
    ) external override {
        require(msg.sender == address(oracle), "Only Oracle");
        require(pendingClaims[requestId] == user, "Unknown request");

        delete pendingClaims[requestId];

        if (verified && !hasClaimed[user]) {
            _sendAirdrop(user);
        } else if (!verified) {
            emit ClaimFailed(user, "Verification failed");
        }
    }

    function _sendAirdrop(address user) internal {
        hasClaimed[user] = true;
        // Send tokens here...
        // token.transfer(user, AIRDROP_AMOUNT);
        emit AirdropClaimed(user, AIRDROP_AMOUNT);
    }

    /// @notice Admin deposits verification fees
    function depositFees() external payable {
        feeEscrow.deposit{value: msg.value}();
    }

    receive() external payable {}
}
```

***

## Quick Reference

```solidity
// Request verification
oracle.requestVerification(dapp, user, claims, maxAge, callback, signature)
    → returns bytes32 requestId

// Check if already verified
oracle.isUserVerified(user, claims, maxAge)
    → returns (bool verified, bytes32 attestationUID, uint256 expiresAt)

// Get signature message hash
oracle.getPermissionMessageHash(user, dapp, claims, maxAge, callback)
    → returns bytes32 messageHash

// Get user nonce (for debugging)
oracle.getUserNonce(user)
    → returns uint256 nonce

// Callback interface (your contract must implement)
onVerificationComplete(requestId, user, verified, attestationUID)
```

***

## Credential Categories

All credentials are organized into these categories:

| Category     | Description                 | Example Claims                                |
| ------------ | --------------------------- | --------------------------------------------- |
| `IDENTITY`   | Identity verification       | `mc_kyc`, `mc_residency` ,`humanity_identity` |
| `SOCIAL`     | Social platform connections | `google_connected`, `twitter_connected`       |
| `FINANCE`    | Financial credentials       | `mc_net_worth`, `binance_finance`             |
| `MEMBERSHIP` | Loyalty program memberships | `marriott_membership`, `delta_membership`     |
| `EMPLOYMENT` | Employment verification     | (Coming soon)                                 |
| `EDUCATION`  | Educational credentials     | (Coming soon)                                 |
| `HEALTH`     | Health-related credentials  | (Coming soon)                                 |
| `REPUTATION` | Reputation scores           | (Coming soon)                                 |
| `LEGAL`      | Legal documents             | (Coming soon)                                 |
| `OWNERSHIP`  | Asset ownership             | (Coming soon)                                 |

***

## Security Notes

1. **Always verify the caller** in your callback function:

   ```solidity
   require(msg.sender == address(oracle), "Unauthorized");
   ```
2. **User signatures are one-time use** - the nonce prevents replay attacks
3. **Signatures are chain-specific** - prevents cross-chain attacks
4. **Use ReentrancyGuard** for functions that handle value transfers
5. **Set appropriate MAX\_AGE** based on your security requirements


---

# 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.humanity.org/build-with-humanity/build-on-chain/credentials-verification-service.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.
