> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/zkp2p/zkp2p-contracts/llms.txt
> Use this file to discover all available pages before exploring further.

# Whitelist Hook

> Pre-intent hook for on-chain address whitelist management

## Overview

`WhitelistPreIntentHook` is a pre-intent hook that restricts intent signaling to a depositor-managed whitelist of taker addresses. It enables "private orderbook" functionality where deposits are visible on-chain but only whitelisted addresses can lock liquidity.

**Contract Location:** `contracts/hooks/WhitelistPreIntentHook.sol`

**Interface:** `IPreIntentHook`

**Compatible With:** OrchestratorV2 (pre-intent hooks only supported in V2)

## Use Cases

### Private Liquidity Pools

Control access to deposit liquidity:

* Restrict intents to trusted partners
* Create private OTC markets
* Enable institutional-only deposits
* Prevent public order sniping

### Relationship-Based Trading

Depositors can:

* Whitelist only known counterparties
* Build trust-based trading networks
* Enforce business relationships on-chain
* Create invite-only markets

### Risk Management

Limit exposure by:

* Whitelisting only KYC'd addresses
* Restricting to verified entities
* Controlling counterparty risk
* Preventing unknown takers

## Architecture

### Per-Deposit Whitelists

Each deposit maintains its own independent whitelist:

```solidity theme={null}
// escrow => depositId => taker => whitelisted
mapping(address => mapping(uint256 => mapping(address => bool))) public whitelist;
```

This allows:

* Different whitelists per deposit
* Fine-grained access control
* Independent management by different depositors

### Authorization Model

Only the deposit owner or delegate can manage the whitelist for their deposit.

## Functions

### addToWhitelist

Adds one or more taker addresses to the whitelist.

```solidity theme={null}
function addToWhitelist(
    address _escrow,
    uint256 _depositId,
    address[] calldata _takers
) external;
```

**Parameters:**

* `_escrow`: Escrow contract address
* `_depositId`: Deposit ID
* `_takers`: Array of taker addresses to whitelist

**Authorization:** Only deposit owner or delegate

**Reverts:**

* `ZeroAddress()`: Escrow or taker address is zero
* `EmptyArray()`: Takers array is empty
* `UnauthorizedCallerOrDelegate()`: Caller is not owner or delegate

**Events:**

```solidity theme={null}
event TakerWhitelisted(
    address indexed escrow,
    uint256 indexed depositId,
    address indexed taker
);
```

**Example:**

```solidity theme={null}
address[] memory takers = new address[](3);
takers[0] = 0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb;
takers[1] = 0x1234567890123456789012345678901234567890;
takers[2] = 0xABCDEF123456789012345678901234567890ABCD;

whitelistHook.addToWhitelist(escrowAddress, depositId, takers);
```

### removeFromWhitelist

Removes one or more taker addresses from the whitelist.

```solidity theme={null}
function removeFromWhitelist(
    address _escrow,
    uint256 _depositId,
    address[] calldata _takers
) external;
```

**Parameters:**

* `_escrow`: Escrow contract address
* `_depositId`: Deposit ID
* `_takers`: Array of taker addresses to remove

**Authorization:** Only deposit owner or delegate

**Reverts:**

* `ZeroAddress()`: Escrow address is zero
* `EmptyArray()`: Takers array is empty
* `UnauthorizedCallerOrDelegate()`: Caller is not owner or delegate
* `TakerNotInWhitelist()`: Taker was not whitelisted (reverts to prevent mistakes)

**Events:**

```solidity theme={null}
event TakerRemovedFromWhitelist(
    address indexed escrow,
    uint256 indexed depositId,
    address indexed taker
);
```

**Example:**

```solidity theme={null}
address[] memory takers = new address[](1);
takers[0] = 0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb;

whitelistHook.removeFromWhitelist(escrowAddress, depositId, takers);
```

### isWhitelisted

Checks if a taker is whitelisted for a specific deposit.

```solidity theme={null}
function isWhitelisted(
    address _escrow,
    uint256 _depositId,
    address _taker
) external view returns (bool);
```

**Returns:** `true` if taker is whitelisted, `false` otherwise

**Example:**

```solidity theme={null}
bool allowed = whitelistHook.isWhitelisted(
    escrowAddress,
    depositId,
    takerAddress
);

if (allowed) {
    // Taker can signal intent
}
```

### validateSignalIntent

Validates taker is whitelisted when intent is signaled (called by orchestrator).

```solidity theme={null}
function validateSignalIntent(
    PreIntentContext calldata _ctx
) external view override;
```

**Validation:**

1. Verify caller is authorized orchestrator
2. Check if `_ctx.taker` is whitelisted for `_ctx.escrow` and `_ctx.depositId`
3. Revert if not whitelisted

**Reverts:**

* `UnauthorizedOrchestratorCaller()`: Caller is not an authorized orchestrator
* `TakerNotWhitelisted()`: Taker is not in the whitelist

## Usage

### Setup: Deploy and Configure

**1. Deploy Hook:**

```solidity theme={null}
WhitelistPreIntentHook hook = new WhitelistPreIntentHook(
    orchestratorRegistryAddress
);
```

**2. Configure Deposit to Use Hook:**

Depositor uses the **dedicated whitelist hook slot**:

```solidity theme={null}
orchestratorV2.setDepositWhitelistHook(
    escrowAddress,
    depositId,
    IPreIntentHook(address(hook))
);
```

<Info>
  OrchestratorV2 has a **dedicated whitelist hook slot** separate from the generic pre-intent hook slot. This allows using whitelist functionality without occupying the general-purpose hook slot.
</Info>

**3. Add Takers to Whitelist:**

```solidity theme={null}
address[] memory takers = new address[](2);
takers[0] = trustedTaker1;
takers[1] = trustedTaker2;

hook.addToWhitelist(escrowAddress, depositId, takers);
```

### Signal Intent (Taker)

Whitelisted takers can signal intents normally:

```solidity theme={null}
SignalIntentParams memory params = SignalIntentParams({
    escrow: escrowAddress,
    depositId: depositId,
    amount: 1000e6,
    to: recipientAddress,
    paymentMethod: keccak256("Venmo"),
    fiatCurrency: keccak256("USD"),
    conversionRate: 1e18,
    payeeId: keccak256("user@venmo.com"),
    referrer: address(0),
    referrerFee: 0,
    postIntentHook: address(0),
    signalHookData: "",
    preIntentHookData: ""  // No data needed for whitelist hook
});

orchestratorV2.signalIntent(params);
```

If taker is not whitelisted, transaction reverts with `TakerNotWhitelisted`.

### Manage Whitelist (Depositor)

**Add Takers:**

```solidity theme={null}
address[] memory newTakers = new address[](1);
newTakers[0] = newTrustedTaker;

hook.addToWhitelist(escrowAddress, depositId, newTakers);
```

**Remove Takers:**

```solidity theme={null}
address[] memory removedTakers = new address[](1);
removedTakers[0] = untrustedTaker;

hook.removeFromWhitelist(escrowAddress, depositId, removedTakers);
```

**Check Status:**

```solidity theme={null}
bool isAllowed = hook.isWhitelisted(escrowAddress, depositId, takerAddress);
```

## Execution Flow

```mermaid theme={null}
sequenceDiagram
    participant Depositor
    participant Orchestrator
    participant WhitelistHook
    participant Taker
    
    Note over Depositor: Setup Phase
    Depositor->>Orchestrator: setDepositWhitelistHook(hook)
    Depositor->>WhitelistHook: addToWhitelist([taker1, taker2])
    WhitelistHook->>WhitelistHook: Store whitelist entries
    
    Note over Taker: Signal Phase (Whitelisted)
    Taker->>Orchestrator: signalIntent()
    Orchestrator->>WhitelistHook: validateSignalIntent()
    WhitelistHook->>WhitelistHook: Check whitelist[taker] == true
    WhitelistHook-->>Orchestrator: pass
    Orchestrator->>Orchestrator: Create intent
    Orchestrator-->>Taker: success
    
    Note over Taker: Signal Phase (Not Whitelisted)
    Taker->>Orchestrator: signalIntent()
    Orchestrator->>WhitelistHook: validateSignalIntent()
    WhitelistHook->>WhitelistHook: Check whitelist[taker] == false
    WhitelistHook-->>Orchestrator: revert TakerNotWhitelisted
    Orchestrator-->>Taker: revert
```

## Events

### TakerWhitelisted

Emitted when a taker is added to whitelist:

```solidity theme={null}
event TakerWhitelisted(
    address indexed escrow,
    uint256 indexed depositId,
    address indexed taker
);
```

**Emitted once per taker** when `addToWhitelist` is called with multiple addresses.

### TakerRemovedFromWhitelist

Emitted when a taker is removed from whitelist:

```solidity theme={null}
event TakerRemovedFromWhitelist(
    address indexed escrow,
    uint256 indexed depositId,
    address indexed taker
);
```

**Emitted once per taker** when `removeFromWhitelist` is called with multiple addresses.

## Errors

```solidity theme={null}
error ZeroAddress();
error EmptyArray();
error UnauthorizedCallerOrDelegate(address caller, address owner, address delegate);
error UnauthorizedOrchestratorCaller(address caller);
error TakerNotWhitelisted(address taker, address escrow, uint256 depositId);
error TakerNotInWhitelist(address taker, address escrow, uint256 depositId);
```

**Error Distinction:**

* `TakerNotWhitelisted`: Thrown during `validateSignalIntent` when taker tries to signal
* `TakerNotInWhitelist`: Thrown during `removeFromWhitelist` when trying to remove non-existent entry

## Gas Optimization

### Batch Operations

Always use batch functions when adding/removing multiple takers:

**Efficient:**

```solidity theme={null}
address[] memory takers = new address[](100);
// ... populate array
hook.addToWhitelist(escrowAddress, depositId, takers);  // Single transaction
```

**Inefficient:**

```solidity theme={null}
for (uint i = 0; i < 100; i++) {
    address[] memory singleTaker = new address[](1);
    singleTaker[0] = takers[i];
    hook.addToWhitelist(escrowAddress, depositId, singleTaker);  // 100 transactions!
}
```

### Storage Costs

Each whitelist entry costs \~20,000 gas (cold SSTORE). For large whitelists:

* Adding 100 addresses: \~2M gas
* Adding 1000 addresses: \~20M gas (may hit block gas limit)

Consider alternative approaches for large whitelists:

* Use [SignatureGatingHook](/contracts/signature-gating-hook) for dynamic/large user sets
* Implement Merkle tree-based whitelist hook for very large sets

## Security Considerations

### Authorization

Only deposit owner or delegate can modify whitelist:

```solidity theme={null}
function _validateDepositorOrDelegate(address _escrow, uint256 _depositId) internal view {
    IEscrow.Deposit memory deposit = IEscrow(_escrow).getDeposit(_depositId);
    bool isDepositorOrDelegate = msg.sender == deposit.depositor
        || (deposit.delegate != address(0) && msg.sender == deposit.delegate);
    if (!isDepositorOrDelegate) {
        revert UnauthorizedCallerOrDelegate(msg.sender, deposit.depositor, deposit.delegate);
    }
}
```

### Orchestrator Validation

Hook verifies caller is authorized orchestrator:

```solidity theme={null}
if (!orchestratorRegistry.isOrchestrator(msg.sender)) {
    revert UnauthorizedOrchestratorCaller(msg.sender);
}
```

### Removal Safety

`removeFromWhitelist` reverts if taker is not whitelisted. This prevents:

* Accidental double-removal
* Typos in removal addresses
* Unclear whitelist state

### Privacy Considerations

Whitelist addresses are **public on-chain**:

* Anyone can query `isWhitelisted`
* Events reveal all whitelisted addresses
* Consider [SignatureGatingHook](/contracts/signature-gating-hook) for private eligibility criteria

## Use Case Examples

### Example 1: Private OTC Market

```solidity theme={null}
// Deploy hook
WhitelistPreIntentHook otcHook = new WhitelistPreIntentHook(orchestratorRegistry);

// Create deposit with whitelist
escrow.deposit(1000000e6, paymentToken, delegate);
orchestratorV2.setDepositWhitelistHook(escrow, depositId, otcHook);

// Whitelist institutional partners
address[] memory institutions = new address[](5);
institutions[0] = jumpTrading;
institutions[1] = galaxyDigital;
institutions[2] = cumberland;
institutions[3] = wintermute;
institutions[4] = flowTraders;

otcHook.addToWhitelist(escrow, depositId, institutions);
```

### Example 2: KYC-Required Deposits

```solidity theme={null}
// Hook for KYC'd users only
WhitelistPreIntentHook kycHook = new WhitelistPreIntentHook(orchestratorRegistry);

// Set hook on deposit
orchestratorV2.setDepositWhitelistHook(escrow, depositId, kycHook);

// As users complete KYC, add them
function onKYCComplete(address user) external onlyKYCProvider {
    address[] memory newUser = new address[](1);
    newUser[0] = user;
    kycHook.addToWhitelist(escrow, depositId, newUser);
}

// If KYC expires or is revoked
function onKYCRevoked(address user) external onlyKYCProvider {
    address[] memory revokedUser = new address[](1);
    revokedUser[0] = user;
    kycHook.removeFromWhitelist(escrow, depositId, revokedUser);
}
```

### Example 3: Invite-Based System

```solidity theme={null}
// Depositor can invite specific users
function inviteUser(address invitee) external {
    // Verify caller is depositor
    require(msg.sender == depositor, "Not depositor");
    
    // Add to whitelist
    address[] memory invitees = new address[](1);
    invitees[0] = invitee;
    whitelistHook.addToWhitelist(escrow, depositId, invitees);
    
    // Notify invitee off-chain
    emit UserInvited(depositId, invitee);
}
```

## Dedicated Whitelist Hook Slot

OrchestratorV2 provides **two pre-intent hook slots** per deposit:

1. **Generic Pre-Intent Hook:** `depositPreIntentHooks[escrow][depositId]`
   * Set via `setDepositPreIntentHook()`
   * For general-purpose validation

2. **Dedicated Whitelist Hook:** `depositWhitelistHooks[escrow][depositId]`
   * Set via `setDepositWhitelistHook()`
   * Specifically for whitelist functionality
   * Allows using whitelist + another hook simultaneously

**Both hooks are executed** during `signalIntent` if set:

```solidity theme={null}
// From OrchestratorV2.sol
_executeHookIfSet(depositWhitelistHooks[_params.escrow][_params.depositId], _params);
_executeHookIfSet(depositPreIntentHooks[_params.escrow][_params.depositId], _params);
```

**Example using both:**

```solidity theme={null}
// Set whitelist hook
orchestratorV2.setDepositWhitelistHook(escrow, depositId, whitelistHook);

// Also set signature gating hook
orchestratorV2.setDepositPreIntentHook(escrow, depositId, signatureGatingHook);

// Now intents must pass BOTH validations:
// 1. Taker must be whitelisted
// 2. Taker must provide valid signature
```

## Comparison with Signature Gating Hook

| Feature         | WhitelistHook             | SignatureGatingHook              |
| --------------- | ------------------------- | -------------------------------- |
| **Storage**     | On-chain whitelist        | Off-chain eligibility            |
| **Gas Cost**    | High (per-address SSTORE) | Low (no state changes)           |
| **Privacy**     | All addresses public      | Criteria can be private          |
| **Scalability** | Limited by gas            | Unlimited                        |
| **Revocation**  | Requires transaction      | Automatic (signature expiration) |
| **Complexity**  | Simple, pure on-chain     | Requires off-chain signer        |
| **Best For**    | Small, stable sets        | Dynamic criteria, large sets     |
| **Management**  | On-chain transactions     | Off-chain API                    |

## Related Contracts

* [Hooks Overview](/contracts/hooks-overview) - Hook system architecture
* [SignatureGatingPreIntentHook](/contracts/signature-gating-hook) - Alternative signature-based gating
* [OrchestratorV2](/contracts/orchestrator) - V2 orchestrator with pre-intent hooks
* [OrchestratorRegistry](/contracts/orchestrator-registry) - Authorized orchestrator registry
* [Escrow](/contracts/escrow) - Deposit management
