# Airdrop Example

This example demonstrates V2 contract integration using the [Airdrop contract](https://github.com/selfxyz/self/blob/main/contracts/contracts/example/Airdrop.sol), with registration/claim phases and Merkle tree token distribution.

### Airdrop-Specific Features

This contract demonstrates:

* **Two-phase distribution:** Registration → Claim separation
* **Merkle tree allocation:** Fair token distribution
* **Multi-document registration (example scope):** E-Passport and EU ID cards in this example contract
* **Anti-duplicate measures:** Nullifier and user identifier tracking

{% hint style="info" %}
Self V2 also supports Aadhaar and KYC attestations. This example keeps document handling focused on E-Passport and EU ID card for simplicity.
{% endhint %}

### Registration Logic

The registration phase validates user eligibility and prevents duplicate registrations:

**Key Validations:**

* Registration phase must be open
* Nullifier hasn't been used (prevents same document registering twice)
* Valid user identifier provided
* User identifier hasn't already registered (prevents address reuse)

### State Variables

```solidity
/// @notice Maps nullifiers to user identifiers for registration tracking
mapping(uint256 nullifier => uint256 userIdentifier) internal _nullifierToUserIdentifier;

/// @notice Maps user identifiers to registration status
mapping(uint256 userIdentifier => bool registered) internal _registeredUserIdentifiers;

/// @notice Tracks addresses that have claimed tokens
mapping(address => bool) public claimed;

/// @notice ERC20 token to be airdropped
IERC20 public immutable token;

/// @notice Merkle root for claim validation
bytes32 public merkleRoot;

/// @notice Phase control
bool public isRegistrationOpen;
bool public isClaimOpen;

/// @notice Verification config ID for identity verification
bytes32 public verificationConfigId;
```

For standard V2 integration patterns (constructor, getConfigId), see [Basic Integration Guide](/self-pass/basic-integration-1.md).

**Registration Verification Hook:**

```solidity
function customVerificationHook(
    ISelfVerificationRoot.GenericDiscloseOutputV2 memory output,
    bytes memory /* userData */
) internal override {
    // Airdrop-specific validations
    if (!isRegistrationOpen) revert RegistrationNotOpen();
    if (_nullifierToUserIdentifier[output.nullifier] != 0) revert RegisteredNullifier();
    if (output.userIdentifier == 0) revert InvalidUserIdentifier();
    if (_registeredUserIdentifiers[output.userIdentifier]) revert UserIdentifierAlreadyRegistered();

    // Register user for airdrop
    _nullifierToUserIdentifier[output.nullifier] = output.userIdentifier;
    _registeredUserIdentifiers[output.userIdentifier] = true;

    emit UserIdentifierRegistered(output.userIdentifier, output.nullifier);
}
```

### Claim Function Implementation

```solidity
function claim(uint256 index, uint256 amount, bytes32[] memory merkleProof) external {
    if (isRegistrationOpen) {
        revert RegistrationNotClosed();
    }
    if (!isClaimOpen) {
        revert ClaimNotOpen();
    }
    if (claimed[msg.sender]) {
        revert AlreadyClaimed();
    }
    if (!_registeredUserIdentifiers[uint256(uint160(msg.sender))]) {
        revert NotRegistered(msg.sender);
    }

    // Verify the Merkle proof
    bytes32 node = keccak256(abi.encodePacked(index, msg.sender, amount));
    if (!MerkleProof.verify(merkleProof, merkleRoot, node)) revert InvalidProof();

    // Mark as claimed and transfer tokens
    claimed[msg.sender] = true;
    token.safeTransfer(msg.sender, amount);

    emit Claimed(index, msg.sender, amount);
}
```

### Configuration Management

The contract includes methods for managing verification configuration:

```solidity
// Set verification config ID
function setConfigId(bytes32 configId) external onlyOwner {
    verificationConfigId = configId;
}

// Override to provide configId for verification
function getConfigId(
    bytes32 destinationChainId,
    bytes32 userIdentifier,
    bytes memory userDefinedData
) public view override returns (bytes32) {
    return verificationConfigId;
}
```

### Administrative Functions

```solidity
// Set Merkle root for claim validation
function setMerkleRoot(bytes32 newMerkleRoot) external onlyOwner;

// Update verification scope
function setScope(uint256 newScope) external onlyOwner;

// Phase control
function openRegistration() external onlyOwner;
function closeRegistration() external onlyOwner;
function openClaim() external onlyOwner;
function closeClaim() external onlyOwner;
```

### Airdrop Flow

1. **Deploy:** Owner deploys with hub address, scope, and token
2. **Configure:** Set verification config ID and Merkle root using `setConfigId()` and `setMerkleRoot()`
3. **Open Registration:** Users prove identity to register
4. **Close Registration:** Move to claim phase
5. **Open Claims:** Registered users claim via Merkle proofs
6. **Distribution Complete:** Tokens distributed to verified users

For verification configuration setup, see [Hub Verification Process](/self-pass/overview/verification-hub.md#v2-enhanced-verifications).


---

# 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.self.xyz/self-pass/basic-integration-1/airdrop-example.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.
