Learn to build privacy-preserving identity verification with Self Protocol - from frontend QR codes to smart contract attestations on Celo.
📺 New to Self? Watch the ETHGlobal Workshop first.
This main branch of the repo contains an example of onchain verification. If you would like to see an example with offchain/backend verification, please check out the 'backend-verification' branch.
main: on chain verificationbackend-verification: off chain/backend verificationhyperlane-example: onchain verification w/ Hyperlane bridging
- Node.js 20+
- Self Mobile App
- Celo wallet with testnet funds
# Clone the boilerplate repository
git clone https://github.com/selfxyz/self-integration-boilerplate.git
cd workshop
# Install frontend dependencies
cd app
npm install
# Install contract dependencies
cd contracts
npm install
forge install foundry-rs/forge-stdNavigate to the contracts folder and configure deployment:
# Create copy of env
cp .env.example .envEdit .env with your values:
# Your private key (with 0x prefix)
PRIVATE_KEY=0xyour_private_key_here
# Network selection
NETWORK=celo-sepolia
# Scope calculation
SCOPE_SEED="self-workshop"Deploy the contract:
# Make script executable
chmod +x script/deploy-proof-of-human.sh
# Deploy contract
./script/deploy-proof-of-human.shThe script will:
- ✅ Build contracts with Foundry
- ✅ Deploy ProofOfHuman contract
- ✅ Verify contract on CeloScan
- ✅ Display deployment summary
Configure the frontend:
Navigate to app folder
# Create copy of env
cp .env.example .envEdit .env:
# Your deployed contract address from Step 2
# IMPORTANT: address should be lowercase
NEXT_PUBLIC_SELF_ENDPOINT=0xyour_contract_address
# App configuration
NEXT_PUBLIC_SELF_APP_NAME="Self Workshop"
NEXT_PUBLIC_SELF_SCOPE_SEED="self-workshop"# Start the Next.js development server
cd app
npm run devVisit http://localhost:3000 to see your verification application!
The Self SDK is configured in your React components (app/app/page.tsx):
import { SelfAppBuilder, countries } from '@selfxyz/qrcode';
const app = new SelfAppBuilder({
version: 2, // Always use V2
appName: process.env.NEXT_PUBLIC_SELF_APP_NAME,
scope: process.env.NEXT_PUBLIC_SELF_SCOPE_SEED,
endpoint: process.env.NEXT_PUBLIC_SELF_ENDPOINT, // Your contract address (must be lowercase)
logoBase64: "https://i.postimg.cc/mrmVf9hm/self.png", // Logo URL or base64
userId: userId, // User's wallet address or identifier
endpointType: "staging_celo", // "staging_celo" for testnet, "celo" for mainnet
userIdType: "hex", // "hex" for Ethereum addresses, "uuid" for UUIDs
userDefinedData: "Hola Buenos Aires!!!", // Optional custom data
disclosures: {
// Verification requirements (must match your contract config)
minimumAge: 18,
excludedCountries: [countries.UNITED_STATES], // Use country constants
// ofac: true, // Optional: OFAC compliance checking
// Optional disclosures (uncomment to request):
// name: true,
// issuing_state: true,
// nationality: true,
// date_of_birth: true,
// passport_number: true,
// gender: true,
// expiry_date: true,
}
}).build();Your contract extends SelfVerificationRoot (contracts/src/ProofOfHuman.sol):
contract ProofOfHuman is SelfVerificationRoot {
// Verification result storage
bool public verificationSuccessful;
address public lastUserAddress;
bytes32 public verificationConfigId;
constructor(
address identityVerificationHubV2Address,
string memory scopeSeed, // Seed used to generate scope
SelfUtils.UnformattedVerificationConfigV2 memory _verificationConfig
) SelfVerificationRoot(identityVerificationHubV2Address, scopeSeed) {
// Format and set verification config
verificationConfig = SelfUtils.formatVerificationConfigV2(_verificationConfig);
verificationConfigId = IIdentityVerificationHubV2(identityVerificationHubV2Address)
.setVerificationConfigV2(verificationConfig);
}
function customVerificationHook(
ISelfVerificationRoot.GenericDiscloseOutputV2 memory output,
bytes memory userData
) internal override {
// Store verification results
verificationSuccessful = true;
lastOutput = output;
lastUserAddress = address(uint160(output.userIdentifier));
emit VerificationCompleted(output, userData);
}
function getConfigId(
bytes32, /* destinationChainId */
bytes32, /* userIdentifier */
bytes memory /* userDefinedData */
) public view override returns (bytes32) {
return verificationConfigId;
}
}- Hub Address:
0x16ECBA51e18a4a7e61fdC417f0d47AFEeDfbed74 - RPC:
https://forno.celo-sepolia.celo-testnet.org - Explorer:
https://celo-sepolia.blockscout.com/ - Supports: Mock passports for testing
- Hub Address:
0xe57F4773bd9c9d8b6Cd70431117d353298B9f5BF - RPC:
https://forno.celo.org - Explorer:
https://celoscan.io - Supports: Real passport verification
- 📱 Telegram Community: Self Protocol Builders Group
- 📖 Documentation: docs.self.xyz
- 🎥 Workshop Video: ETHGlobal Buenos Aires
self-integration-example/
├── app/ # Next.js frontend application
│ ├── app/
│ │ ├── page.tsx # Main QR code page with Self SDK integration
│ │ ├── layout.tsx # Root layout with metadata
│ │ ├── globals.css # Global styles
│ │ └── verified/
│ │ ├── page.tsx # Success page after verification
│ │ └── page.module.css # Success page styles
│ ├── .env.example # Frontend environment template
│ ├── package.json # Frontend dependencies
│ ├── tailwind.config.ts # Tailwind CSS configuration
│ └── README.md # Frontend documentation
│
├── contracts/ # Foundry smart contracts
│ ├── src/
│ │ └── ProofOfHuman.sol # Main verification contract
│ ├── script/
│ │ ├── Base.s.sol # Base script utilities
│ │ ├── DeployProofOfHuman.s.sol # Foundry deployment script
│ │ └── deploy-proof-of-human.sh # Automated deployment script
│ ├── lib/ # Dependencies
│ │ ├── forge-std/ # Foundry standard library
│ │ └── openzeppelin-contracts/ # OpenZeppelin contracts
│ ├── .env.example # Contract environment template
│ ├── foundry.toml # Foundry configuration
│ └── package.json # Contract dependencies
│
└── README.md # This file
- Self Protocol Docs - Complete protocol documentation
- Contract Integration Guide - Smart contract specifics
- Frontend SDK Reference - Frontend integration details
- Disclosure Proofs - Available verification options
- Self on iOS - iOS App
- Self on Android - Android App