Basic Integration
This document provides an overview and integration guide for our smart contract, available as an npm package. You can install it with:
npm install @selfxyz/contracts
V2 Integration
Package Structure
The V2 package supports multiple document types with enhanced verification architecture:
.
├── abstract
│ └── SelfVerificationRoot.sol # Base impl in self verification V2
├── constants
│ ├── AttestationId.sol # Unique identifiers for identity documents (E_PASSPORT, EU_ID_CARD)
│ └── CircuitConstantsV2.sol # V2 indices for public signals in our circuits
├── interfaces # Interfaces for V2 contracts
│ ├── IDscCircuitVerifier.sol
│ ├── IIdentityRegistryV1.sol
│ ├── IIdentityRegistryIdCardV1.sol # New: EU ID Card registry interface
│ ├── IIdentityVerificationHubV2.sol # V2 hub interface
│ ├── IRegisterCircuitVerifier.sol
│ ├── ISelfVerificationRoot.sol
│ └── IVcAndDiscloseCircuitVerifier.sol
├── libraries
│ ├── SelfStructs.sol # V2 data structures for verification
│ ├── CustomVerifier.sol # Custom verification logic for different document types
│ ├── CircuitAttributeHandlerV2.sol # V2 attribute extraction
│ ├── GenericFormatter.sol # V2 output formatting
│ └── Formatter.sol # Utility functions (maintained for compatibility)
└── example
├── HappyBirthday.sol # Updated V2 example supporting both passports and EU ID cards
├── Airdrop.sol # V2 airdrop example
└── SelfIdentityERC721.sol # NFT example with identity verification
Step-by-Step Integration Guide
Step 1: Install Dependencies
npm install @selfxyz/contracts
Step 2: Create Your Contract
Extend SelfVerificationRoot
and implement the required methods:
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {SelfVerificationRoot} from "@selfxyz/contracts/contracts/abstract/SelfVerificationRoot.sol";
import {ISelfVerificationRoot} from "@selfxyz/contracts/contracts/interfaces/ISelfVerificationRoot.sol";
/**
* @title ProofOfHuman
* @notice Simple example showing how to verify human identity using Self Protocol
*/
contract ProofOfHuman is SelfVerificationRoot {
// Store verification status for each user
mapping(address => bool) public verifiedHumans;
bytes32 public verificationConfigId;
address public lastUserAddress;
// Event to track successful verifications
event VerificationCompleted(
ISelfVerificationRoot.GenericDiscloseOutputV2 output,
bytes userData
);
/**
* @notice Constructor for the contract
* @param _identityVerificationHubV2Address The address of the Identity Verification Hub V2
* @param _scope The scope of the contract
* @param _verificationConfigId The configuration ID for the contract
*/
constructor(
address _identityVerificationHubV2Address,
uint256 _scope,
bytes32 _verificationConfigId
) SelfVerificationRoot(_identityVerificationHubV2Address, _scope) {
verificationConfigId = _verificationConfigId;
}
/**
* @notice Implementation of customVerificationHook
* @dev This function is called by onVerificationSuccess after hub address validation
* @param output The verification output from the hub
* @param userData The user data passed through verification
*/
function customVerificationHook(
ISelfVerificationRoot.GenericDiscloseOutputV2 memory _output,
bytes memory _userData
) internal override {
lastUserAddress = address(uint160(_output.userIdentifier));
verifiedHumans[lastUserAddress] = true;
emit VerificationCompleted(_output, _userData);
// Add your custom logic here:
// - Mint NFT to verified user
// - Add to allowlist
// - Transfer tokens
// - etc.
}
function getConfigId(
bytes32 _destinationChainId,
bytes32 _userIdentifier,
bytes memory _userDefinedData
) public view override returns (bytes32) {
return verificationConfigId;
}
/**
* @notice Check if an address is a verified human
*/
function isVerifiedHuman(address _user) external view returns (bool) {
return verifiedHumans[_user];
}
function setConfigId(bytes32 _configId) external {
verificationConfigId = _configId;
}
}
Step 3: Generate Configuration ID
Use the Self Configuration Tools to easily create your verification configuration and generate a config ID. This tool allows you to configure age requirements, country restrictions, and OFAC checks with a user-friendly interface.
Once you have your config ID from the tool, you can use it in your contract in several ways:
Option 1: Hard-coded
function getConfigId(
bytes32 _destinationChainId,
bytes32 _userIdentifier,
bytes memory _userDefinedData
) public view override returns (bytes32) {
// Replace with your actual config ID from the tool
return 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef;
}
Option 2: Store and update via setter
/**
* @notice Expose the internal _setScope function for testing
* @param newScope The new scope value to set
*/
function setScope(uint256 newScope) external {
_setScope(newScope);
}
⚠️ Security Note: In production environments, you should add proper access control to the
setScope
function (e.g.,onlyOwner
modifier) to prevent unauthorized scope modifications. The example above is suitable for testing but should not be deployed without access restrictions.
Option 3: Dynamic based on user context
This advanced approach allows your contract to use different verification configurations based on user actions and context. Instead of having a single, fixed config ID, you can dynamically select which verification requirements to apply based on what the user is trying to do.
Why use dynamic config IDs?
Different actions may require different verification levels (e.g., voting vs. general access)
External contracts might need specific identity requirements
You can support multiple use cases in a single contract
Example scenario: Users can join polls (actionType 0) with nationality verification, or get general verification (actionType 1) with basic identity proof.
Step 4: Deploy Your Contract
Deploy with the V2 Hub address:
Celo Mainnet:
0xe57F4773bd9c9d8b6Cd70431117d353298B9f5BF
Celo Testnet:
0x68c931C9a534D37aa78094877F46fE46a49F1A51
Step 5: Set Up Scope
Your contract needs a proper scope for verification. You have two approaches:
Option 1: Predict address with CREATE2 (advanced)
// Calculate scope before deployment using predicted address
// Use tools.self.xyz to calculate scope with your predicted contract address
Option 2: Update scope after deployment (easier)
/**
* @notice Expose the internal _setScope function for testing
* @param newScope The new scope value to set
*/
function setScope(uint256 newScope) external {
_setScope(newScope);
}
After deployment, use the Self Configuration Tools to calculate the actual scope with your deployed contract address and update it using the setter function.
Step 6: Configure Frontend SDK
Set up your frontend with the deployed contract address (see Frontend Configuration).
Next Steps
Identity Attributes - Learn how to access verified user data
Frontend Configuration - Set up your frontend integration
Example Contracts - See complete implementation examples
Last updated