# IVerificationOracle

## Contract address

| Testnet | `0x67c0A5cA2Fb19E8E0Ff008d727aff5f128b00E09` |
| ------- | -------------------------------------------- |
| Mainnet | `0x8D71D8bD47860bd0381b272AE42162c3692c4F3a` |

## Overview

`IHumanityVerificationOracle` provides the core verification functionality for Humanity. It enables DApps to request human verification for users, validates signatures, manages verification requests via EAS attestations, coordinates fee payments through FeeEscrow, and notifies DApps of results via callbacks.

### Core Concepts

| Concept               | Description                                                                               |
| --------------------- | ----------------------------------------------------------------------------------------- |
| **Request Lifecycle** | Each verification request transitions through states: `Pending` → `Done` or `Cancelled`   |
| **EAS Integration**   | Requests and results are recorded as EAS attestations for auditability and on-chain proof |
| **User Consent**      | Users must sign a permission message off-chain before verification can be requested       |
| **DApp Callback**     | Upon completion, the Oracle notifies the DApp via `onVerificationComplete()`              |

### Request Status

```solidity
enum RequestStatus {
    Pending,    // Verification in progress
    Done,       // Verification completed
    Cancelled   // Request cancelled or revoked
}
```

### Verification Request Structure

```solidity
struct VerificationRequest {
    bytes32 attestationUID;      // EAS attestation UID for this request
    address requester;           // DApp that requested verification
    address user;                // User being verified
    address attester;            // Address that created the attestation
    uint256 expirationTime;      // When the verification expires
    uint256 revocationTime;      // When revoked (0 if not revoked)
    string[] requiredClaims;     // Claims required for verification
    uint256 maxAge;              // Maximum age of verification data
    uint256 fee;                 // Fee paid for verification
    address callbackContract;    // Contract to notify on completion
    RequestStatus status;        // Current status
    uint256 timestamp;           // When request was created
}
```

***

### Verification Flow

#### **1. PREPARE**

DApp determines the `requiredClaims[]` array and `maxAge` parameter for the verification request.

#### **2. GET PERMISSION HASH**

DApp calls `getPermissionMessageHash(user, dapp, claims, maxAge, callbackContract)` to generate the message hash that the user must sign.

#### **3. USER SIGNS**

User signs the `messageHash` off-chain using their wallet, producing `userSignature`.

#### **4. REQUEST VERIFICATION**

DApp calls `requestVerification(dapp, user, claims, maxAge, callbackContract, signature)`. The Oracle validates the signature, reserves the fee via `FeeEscrow.reserveForRequest()`, and returns a `requestId` (bytes32).

#### **5. ORACLE PROCESSES**

The Oracle performs off-chain verification of the user's humanity credentials and creates an EAS attestation for the result.

#### **6. SUBMIT RESULT**

Oracle calls `submitVerificationResult(requestUID, verified, ...)`. This settles fees via `FeeEscrow.settleRequest()` and emits a `VerificationCompleted` event.

#### **7. CALLBACK**

Oracle calls the DApp's `onVerificationComplete(requestId, user, verified, attestationUID)` callback. The DApp grants or denies access based on the `verified` boolean.

***

## Interface

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

interface IHumanityVerificationOracle is IHumanityVerificationOracleErrors {
    // Request Management
    function requestVerification(
        address dapp,
        address user,
        string[] memory requiredClaims,
        uint256 maxAge,
        address callbackContract,
        bytes memory userSignature
    ) external returns (bytes32 requestId);

    function submitVerificationResult(
        bytes32 requestUID,
        bool verified,
        uint256 expiresAt,
        string[] memory usedIssuers,
        string[] memory usedCategories
    ) external;

    function revokeVerificationRequest(bytes32 attestationUID) external;

    // Verification Queries
    function isUserVerified(
        address user,
        string[] memory requiredClaims,
        uint256 maxAge
    ) external view returns (bool verified, bytes32 attestationUID, uint256 expiresAt);

    function getRequest(bytes32 requestUID) external view returns (VerificationRequest memory);
    function getResult(bytes32 resultUID) external view returns (...);
    function getUserRequests(address user) external view returns (bytes32[] memory);
    function resultToRequest(bytes32 resultUID) external view returns (bytes32 requestUID);

    // Signature Helpers
    function getPermissionMessageHash(
        address user,
        address dapp,
        string[] memory requiredClaims,
        uint256 maxAge,
        address callbackContract
    ) external view returns (bytes32);

    function getUserNonce(address user) external view returns (uint256);

    // Attestation Decoders
    function decodeRequestAttestation(bytes memory attestationData) external pure returns (...);
    function decodeResultAttestation(bytes memory attestationData) external pure returns (...);

    // Admin Functions
    function updateVerificationFee(uint256 _newFee) external;
    function updateTreasuryAddress(address _newTreasury) external;
    function updateEAS(address _newEAS) external;
    function updateSchemas(bytes32 _newRequestSchemaUID, bytes32 _newResultSchemaUID) external;
    function pause() external;
    function unpause() external;
}
```

***

## Functions

### Request Management

#### `requestVerification(...)`

Request verification for a user. Requires a valid user signature.

```solidity
function requestVerification(
    address dapp,
    address user,
    string[] memory requiredClaims,
    uint256 maxAge,
    address callbackContract,
    bytes memory userSignature
) external returns (bytes32 requestId);
```

| Parameter          | Type       | Description                                                   |
| ------------------ | ---------- | ------------------------------------------------------------- |
| `dapp`             | `address`  | Address of the DApp requesting verification                   |
| `user`             | `address`  | Address of the user to verify                                 |
| `requiredClaims`   | `string[]` | Array of claim types required (e.g., `["humanity_identity"]`) |
| `maxAge`           | `uint256`  | Maximum age of verification data in seconds                   |
| `callbackContract` | `address`  | Contract to receive verification callback                     |
| `userSignature`    | `bytes`    | User’s signature authorizing the verification                 |

**Returns:** `requestId` - The unique identifier (bytes32) for this verification request

**Reverts:**

* `UnauthorizedRequest` if signature is invalid
* `NoClaimsSpecified` if `requiredClaims` is empty
* `InsufficientAvailableBalance` (from FeeEscrow) if DApp hasn’t pre-funded

**Emits:** `VerificationRequested(bytes32 indexed attestationUID, address indexed requester, address indexed user, string[] requiredClaims, uint256 fee)`

***

#### `submitVerificationResult(...)`

Submit the result of a verification request. Called by the Oracle after processing.

```solidity
function submitVerificationResult(
    bytes32 requestUID,
    bool verified,
    uint256 expiresAt,
    string[] memory usedIssuers,
    string[] memory usedCategories
) external;
```

| Parameter        | Type       | Description                                 |
| ---------------- | ---------- | ------------------------------------------- |
| `requestUID`     | `bytes32`  | The request to submit results for           |
| `verified`       | `bool`     | Whether verification succeeded              |
| `expiresAt`      | `uint256`  | Timestamp when this verification expires    |
| `usedIssuers`    | `string[]` | DIDs of issuers whose credentials were used |
| `usedCategories` | `string[]` | Categories of claims that were verified     |

**Emits:** `VerificationCompleted(bytes32 indexed requestUID, bool verified, bytes32 resultUID)`

***

#### `revokeVerificationRequest(bytes32 attestationUID)`

Revoke a verification request or result.

```solidity
function revokeVerificationRequest(bytes32 attestationUID) external;
```

**Reverts:**

* `RequestNotFound` if request doesn’t exist
* `RequestAlreadyRevoked` if already revoked
* `NotAuthorizedToRevoke` if caller lacks permission

**Emits:** `VerificationRequestRevoked(address indexed revoker, bytes32 indexed requestUID, uint256 revokedAt)`

***

### Verification Queries

#### `isUserVerified(...)`

Check if a user has a valid verification matching the specified criteria.

```solidity
function isUserVerified(
    address user,
    string[] memory requiredClaims,
    uint256 maxAge
) external view returns (
    bool verified,
    bytes32 attestationUID,
    uint256 expiresAt
);
```

| Parameter        | Type       | Description                            |
| ---------------- | ---------- | -------------------------------------- |
| `user`           | `address`  | Address of the user to check           |
| `requiredClaims` | `string[]` | Claims that must be verified           |
| `maxAge`         | `uint256`  | Maximum age of verification in seconds |

**Returns:**

* `verified` - Whether user has valid verification
* `attestationUID` - UID of the verification attestation (if verified)
* `expiresAt` - When the verification expires

***

#### `getRequest(bytes32 requestUID)`

Get full details of a verification request.

```solidity
function getRequest(bytes32 requestUID) external view returns (VerificationRequest memory request);
```

***

#### `getResult(bytes32 resultUID)`

Get details of a verification result.

```solidity
function getResult(bytes32 resultUID) external view returns (
    address user,
    address requester,
    bytes32 requestUID,
    bool verified,
    uint256 expiresAt,
    uint256 revocationTime,
    string[] memory usedIssuers,
    string[] memory usedCategories
);
```

***

#### `getUserRequests(address user)`

Get all verification request UIDs for a user.

```solidity
function getUserRequests(address user) external view returns (bytes32[] memory);
```

***

#### `resultToRequest(bytes32 resultUID)`

Map a result UID back to its original request UID.

```solidity
function resultToRequest(bytes32 resultUID) external view returns (bytes32 requestUID);
```

***

### Signature Helpers

#### `getPermissionMessageHash(...)`

Get the message hash that a user must sign to authorize verification.

```solidity
function getPermissionMessageHash(
    address user,
    address dapp,
    string[] memory requiredClaims,
    uint256 maxAge,
    address callbackContract
) external view returns (bytes32);
```

> Important: The signature must be computed over the exact tuple (user, dapp, requiredClaims\[], maxAge, callbackContract, currentUserNonce). Any mismatch causes UnauthorizedRequest.

***

#### `getUserNonce(address user)`

Get the current nonce for a user. The nonce is incremented after each verification request.

```solidity
function getUserNonce(address user) external view returns (uint256);
```

***

### Attestation Decoders

#### `decodeRequestAttestation(bytes memory attestationData)`

Decode EAS-encoded request attestation data.

```solidity
function decodeRequestAttestation(bytes memory attestationData) external pure returns (
    address requester,
    address user,
    string[] memory requiredClaims,
    uint256 maxAge,
    uint256 fee,
    address callbackContract
);
```

***

#### `decodeResultAttestation(bytes memory attestationData)`

Decode EAS-encoded result attestation data.

```solidity
function decodeResultAttestation(bytes memory attestationData) external pure returns (
    bytes32 requestUID,
    bool verified,
    uint256 expiresAt,
    string[] memory usedIssuers,
    string[] memory usedCategories
);
```

***

### Admin Functions

#### `updateVerificationFee(uint256 _newFee)`

Update the fee required for verification requests.

```solidity
function updateVerificationFee(uint256 _newFee) external;
```

**Emits:** `VerificationFeeUpdated(uint256 newFee)`

***

#### `updateTreasuryAddress(address _newTreasury)`

Update the treasury address for fee collection.

```solidity
function updateTreasuryAddress(address _newTreasury) external;
```

**Emits:** `TreasuryAddressUpdated(address newTreasury)`

***

#### `updateEAS(address _newEAS)`

Update the EAS contract address.

```solidity
function updateEAS(address _newEAS) external;
```

**Emits:** `EASAddressUpdated(address newEAS)`

***

#### `updateSchemas(bytes32 _newRequestSchemaUID, bytes32 _newResultSchemaUID)`

Update the EAS schema UIDs for requests and results.

```solidity
function updateSchemas(bytes32 _newRequestSchemaUID, bytes32 _newResultSchemaUID) external;
```

**Emits:** `SchemasUpdated(bytes32 oldRequestSchemaUID, bytes32 newRequestSchemaUID, bytes32 oldResultSchemaUID, bytes32 newResultSchemaUID)`

***

#### `pause()` / `unpause()`

Pause or unpause contract operations.

```solidity
function pause() external;
function unpause() external;
```

***

### DApp Callback Interface

DApps must implement `IHumanityVerificationCallback` to receive verification results.

```solidity
interface IHumanityVerificationCallback {
    function onVerificationComplete(
        bytes32 requestId,
        address user,
        bool verified,
        bytes32 attestationUID
    ) external;
}
```

| Parameter        | Type      | Description                        |
| ---------------- | --------- | ---------------------------------- |
| `requestId`      | `bytes32` | The verification request ID        |
| `user`           | `address` | The user who was verified          |
| `verified`       | `bool`    | Whether verification succeeded     |
| `attestationUID` | `bytes32` | EAS attestation UID for the result |

***

## Events

### Lifecycle Events

| Event                                                                                                                                          | Description                      |
| ---------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- |
| `VerificationRequested(bytes32 indexed attestationUID, address indexed requester, address indexed user, string[] requiredClaims, uint256 fee)` | New verification request created |
| `VerificationCompleted(bytes32 indexed requestUID, bool verified, bytes32 resultUID)`                                                          | Verification completed           |
| `VerificationRequestRevoked(address indexed revoker, bytes32 indexed requestUID, uint256 revokedAt)`                                           | Request revoked                  |
| `VerificationResultRevoked(address indexed revoker, bytes32 indexed resultUID, uint256 revokedAt)`                                             | Result revoked                   |

### Callback Events

| Event                                                                                             | Description                          |
| ------------------------------------------------------------------------------------------------- | ------------------------------------ |
| `CallbackErrorString(bytes32 indexed requestUID, address callbackContract, string reason)`        | Callback failed with revert string   |
| `CallbackErrorPanic(bytes32 indexed requestUID, address callbackContract, uint256 errorCode)`     | Callback failed with panic           |
| `CallbackErrorLowLevel(bytes32 indexed requestUID, address callbackContract, bytes lowLevelData)` | Callback failed with low-level error |

### Fee Events

| Event                                                                                                                                                             | Description                      |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- |
| `FeeCollected(address indexed dapp, uint256 feePaid, uint256 debtPaid)`                                                                                           | Fee collected from DApp          |
| `FeesDistributed(address dapp, bytes32 verificationUID, address[] issuerAddresses, uint256[] issuersFee, uint256 stakingFee, uint256 proofFee, uint256 totalFee)` | Fees distributed to stakeholders |
| `FeeVerifierPaid(address indexed dapp, uint256 fee, uint256 debt)`                                                                                                | Verifier paid                    |
| `FeesWithdrawn(address indexed recipient, uint256 amount)`                                                                                                        | Fees withdrawn                   |
| `DebtUpdated(address indexed dapp, uint256 oldDebt, uint256 newDebt)`                                                                                             | DApp debt updated                |

### Configuration Events

| Event                                                                    | Description                  |
| ------------------------------------------------------------------------ | ---------------------------- |
| `VerificationFeeUpdated(uint256 newFee)`                                 | Fee amount changed           |
| `TreasuryAddressUpdated(address newTreasury)`                            | Treasury address changed     |
| `EASAddressUpdated(address newEAS)`                                      | EAS contract address changed |
| `FeeEscrowUpdated(address indexed oldEscrow, address indexed newEscrow)` | FeeEscrow address changed    |
| `SchemasUpdated(...)`                                                    | EAS schemas changed          |

***

## Errors

### Configuration Errors

| Error                              | Description                        |
| ---------------------------------- | ---------------------------------- |
| `AlreadyProcessing()`              | Request is already being processed |
| `InvalidAddress(string param)`     | Provided address is invalid        |
| `SchemasAlreadyInitialized()`      | Schemas already set                |
| `SchemasNotInitialized()`          | Schemas not yet configured         |
| `InvalidSchema(string schemaType)` | Invalid schema provided            |

### Request Errors

| Error                                                             | Description                             |
| ----------------------------------------------------------------- | --------------------------------------- |
| `InvalidUser()`                                                   | User address is invalid                 |
| `InvalidRequest()`                                                | Request data is invalid                 |
| `InvalidValue(string param)`                                      | Parameter value is invalid              |
| `NoClaimsSpecified()`                                             | Required claims array is empty          |
| `InsufficientVerificationFee(uint256 required, uint256 provided)` | Fee provided is insufficient            |
| `RequestNotFound(bytes32 requestUID)`                             | Request doesn’t exist                   |
| `RequestExpired(bytes32 requestUID)`                              | Request has expired                     |
| `RequestAlreadyRevoked(bytes32 requestUID)`                       | Request was already revoked             |
| `BatchSizeExceedsLimit(uint256 size, uint256 maxSize)`            | Batch too large                         |
| `MismatchedArrayLengths()`                                        | Array parameters have different lengths |
| `DebtLimitExceeded(uint256 newDebt, uint256 maxDebtAllowance)`    | DApp debt would exceed limit            |

### Authorization Errors

| Error                                                               | Description                             |
| ------------------------------------------------------------------- | --------------------------------------- |
| `NotAuthorizedToRevoke(address caller)`                             | Caller cannot revoke this request       |
| `UnauthorizedUpgrade(address caller)`                               | Caller cannot upgrade contract          |
| `UnauthorizedRequest(address expectedSigner, address actualSigner)` | Signature doesn’t match expected signer |

### Transfer Errors

| Error                                                      | Description                      |
| ---------------------------------------------------------- | -------------------------------- |
| `NoFeesToWithdraw()`                                       | No fees available for withdrawal |
| `RefundFailed(address recipient, uint256 amount)`          | Refund transfer failed           |
| `TreasuryTransferFailed(address treasury, uint256 amount)` | Treasury transfer failed         |
| `LockFeeFailed(address issuer)`                            | Failed to lock fee for issuer    |

## Integration Example

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

import "./interfaces/verificationOracle/IVerificationOracle.sol";
import "./interfaces/feeEscrow/IFeeEscrow.sol";

contract MyVerifiedDApp is IHumanityVerificationCallback {
    IHumanityVerificationOracle public oracle;
    IFeeEscrow public feeEscrow;

    mapping(address => bool) public verifiedUsers;
    mapping(bytes32 => address) public pendingRequests;

    string[] public requiredClaims = ["humanity_identity"];
    uint256 public constant MAX_VERIFICATION_AGE = 30 days;

    constructor(address _oracle, address _feeEscrow) {
        oracle = IHumanityVerificationOracle(_oracle);
        feeEscrow = IFeeEscrow(_feeEscrow);
    }

    // Step 1: Get the message hash for user to sign
    function getPermissionHash(address user) external view returns (bytes32) {
        return oracle.getPermissionMessageHash(
            user,
            address(this),
            requiredClaims,
            MAX_VERIFICATION_AGE,
            address(this)
        );
    }

    // Step 2: Request verification with user's signature
    function requestVerification(
        address user,
        bytes memory userSignature
    ) external returns (bytes32 requestId) {
        // Ensure we have funds in escrow
        require(
            feeEscrow.getAvailable(address(this)) > 0,
            "Insufficient escrow balance"
        );

        requestId = oracle.requestVerification(
            address(this),
            user,
            requiredClaims,
            MAX_VERIFICATION_AGE,
            address(this),
            userSignature
        );

        pendingRequests[requestId] = user;
    }

    // Step 3: Receive callback from Oracle
    function onVerificationComplete(
        bytes32 requestId,
        address user,
        bool verified,
        bytes32 attestationUID
    ) external override {
        require(msg.sender == address(oracle), "Only oracle can callback");

        if (verified) {
            verifiedUsers[user] = true;
            // Grant access, mint tokens, etc.
        }

        delete pendingRequests[requestId];
    }

    // Check if user is already verified
    function checkVerification(address user) external view returns (bool) {
        (bool verified, , ) = oracle.isUserVerified(
            user,
            requiredClaims,
            MAX_VERIFICATION_AGE
        );
        return verified;
    }

    // Fund the escrow
    function fundEscrow() external payable {
        feeEscrow.deposit{value: msg.value}();
    }
}
```

***

## Integration Tips

1. **Signature Validation**: The `userSignature` must be computed over the exact tuple `(user, dapp, requiredClaims[], maxAge, callbackContract, currentUserNonce)`. Any mismatch causes `UnauthorizedRequest` and reverts.
2. **FeeEscrow Configuration**: Ensure `FeeEscrow.verificationOracle` is configured to this Oracle instance. Otherwise, `reserveForRequest`/`settleRequest` will revert with `UnauthorizedCaller`.
3. **Insufficient Balance Error**: If you see a revert with data starting `0x6701c6f6`, it decodes to `InsufficientAvailableBalance(dapp, required, available)` from IFeeEscrow. Pre-fund FeeEscrow via `deposit` or lower the `verificationFee` (admin).
4. **Callback Handling**: Treat callback execution as untrusted. Handle errors gracefully and consider timeouts/compensation logic if callbacks revert.
5. **Upgrades (UUPS)**: Always append new state variables at the end when upgrading. Do not increase `__gap`; reduce it by the number of slots you add.
6. **Nonce Management**: Each verification request increments the user’s nonce. If a signature fails, check that you’re using the current nonce from `getUserNonce(user)`.

***


---

# 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/canonical-contracts/iverificationoracle.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.
