Skip to main content

Introduction

The zkp2p-contracts hook system provides extensible lifecycle integration points that allow custom logic to execute during intent operations. Hooks enable features like cross-chain bridging, access control, and custom validation without modifying core protocol contracts.

Hook Types

The protocol supports two types of hooks:

Pre-Intent Hooks

Pre-intent hooks execute during signalIntent before state changes are committed. They are used for validation and access control. Interface: IPreIntentHook Use Cases:
  • Validating taker eligibility
  • Enforcing whitelist restrictions
  • Signature-based gating
  • Custom authorization logic
Key Characteristics:
  • Called before intent is created
  • Should revert to reject the intent
  • Read-only validation (no state modifications expected)
  • Can access full intent context via PreIntentContext

Post-Intent Hooks

Post-intent hooks execute during fulfillIntent after core protocol logic completes. They handle funds and execute custom actions with the released tokens. Interfaces: IPostIntentHook (V1) and IPostIntentHookV2 (V2) Use Cases:
  • Bridging tokens to other chains
  • Custom fund distribution
  • Protocol integrations
  • Automated DeFi operations
Key Characteristics:
  • Called after intent is fulfilled
  • Receives tokens from the orchestrator
  • Can perform arbitrary operations with funds
  • V2 uses structured context for better composability

Hook Execution Flow

Pre-Intent Hook Flow

Post-Intent Hook Flow

Interface Reference

IPreIntentHook

interface IPreIntentHook {
    struct PreIntentContext {
        address taker;
        address escrow;
        uint256 depositId;
        uint256 amount;
        address to;
        bytes32 paymentMethod;
        bytes32 fiatCurrency;
        uint256 conversionRate;
        address referrer;
        uint256 referrerFee;
        bytes preIntentHookData;
    }

    function validateSignalIntent(PreIntentContext calldata _ctx) external;
}
Parameters:
  • taker - Address signaling the intent
  • escrow - Escrow contract holding the deposit
  • depositId - ID of the deposit being locked
  • amount - Amount to lock from the deposit
  • to - Recipient address for the funds
  • paymentMethod - Payment method identifier (e.g., Venmo, PayPal)
  • fiatCurrency - Fiat currency code (e.g., USD, EUR)
  • conversionRate - Exchange rate (scaled)
  • referrer - Referrer address for fee sharing
  • referrerFee - Fee allocated to referrer
  • preIntentHookData - Custom data passed from signalIntent call

IPostIntentHook (V1)

interface IPostIntentHook {
    function execute(
        IOrchestrator.Intent memory _intent,
        uint256 _amountNetFees,
        bytes calldata _fulfillIntentData
    ) external;
}
Parameters:
  • _intent - Full intent data structure
  • _amountNetFees - Amount after fees have been deducted
  • _fulfillIntentData - Custom data passed from fulfillIntent call

IPostIntentHookV2

interface IPostIntentHookV2 {
    struct HookIntentContext {
        address owner;
        address to;
        address escrow;
        uint256 depositId;
        uint256 amount;
        uint256 timestamp;
        bytes32 paymentMethod;
        bytes32 fiatCurrency;
        uint256 conversionRate;
        bytes32 payeeId;
        bytes signalHookData;
    }

    struct HookExecutionContext {
        bytes32 intentHash;
        address token;
        uint256 executableAmount;
        HookIntentContext intent;
    }

    function execute(
        HookExecutionContext calldata _ctx,
        bytes calldata _fulfillHookData
    ) external;
}
V2 Improvements:
  • Structured context instead of raw Intent struct
  • Separates signal-time data (signalHookData) from fulfill-time data (fulfillHookData)
  • Includes intentHash for event correlation
  • Better typing with dedicated context structs

Hook Configuration

Setting Pre-Intent Hooks (V2 Only)

In OrchestratorV2, depositors can set hooks per deposit:
// Set generic pre-intent hook
orchestratorV2.setDepositPreIntentHook(
    escrowAddress,
    depositId,
    hookAddress
);

// Set dedicated whitelist hook (separate slot)
orchestratorV2.setDepositWhitelistHook(
    escrowAddress,
    depositId,
    whitelistHookAddress
);
Authorization: Only the deposit owner or delegate can set hooks.

Setting Post-Intent Hooks

Post-intent hooks are specified during signalIntent: V1 (Orchestrator):
struct Intent {
    // ... other fields
    address postIntentHook;
    bytes data; // Hook-specific commitment data
}
V2 (OrchestratorV2):
struct SignalIntentParams {
    // ... other fields
    address postIntentHook;
    bytes signalHookData; // Commitment data set at signal time
}

struct FulfillIntentParams {
    // ... other fields
    bytes fulfillHookData; // JIT data provided at fulfill time
}

Security Considerations

Pre-Intent Hook Security

  1. Reentrancy Protection: Hooks are called within a reentrancy-protected context
  2. Gas Limits: Complex validation may hit gas limits; keep logic simple
  3. DoS Resistance: Hooks should not depend on external state that could be manipulated
  4. Authorization: Always verify msg.sender is an authorized orchestrator

Post-Intent Hook Security

  1. Token Handling: Hooks must pull exact approved amount to prevent stranded funds
  2. Fallback Logic: Consider graceful degradation instead of reverting (see AcrossBridgeHook)
  3. Authorization: Verify caller is authorized orchestrator
  4. Approval Management: Always reset approvals after operations
  5. External Calls: Use try-catch for external protocol interactions

Implementation Examples

See the following pages for detailed hook implementations:

Hook Development Best Practices

For Pre-Intent Hooks

  1. Fail Fast: Revert immediately on validation failure
  2. Minimal State: Avoid complex state reads when possible
  3. Clear Errors: Use descriptive custom errors
  4. Registry Check: Always validate orchestrator authorization
function validateSignalIntent(PreIntentContext calldata _ctx) external view {
    if (!orchestratorRegistry.isOrchestrator(msg.sender)) {
        revert UnauthorizedCaller(msg.sender);
    }
    
    // Your validation logic
    if (!isValid(_ctx)) {
        revert ValidationFailed();
    }
}

For Post-Intent Hooks

  1. Exact Pulls: Pull exactly the approved amount
  2. Approval Hygiene: Reset approvals after operations
  3. Graceful Degradation: Consider fallback behavior instead of reverting
  4. Event Emission: Emit detailed events for tracking
  5. Rescue Functions: Implement admin rescue for stuck funds
function execute(
    HookExecutionContext calldata _ctx,
    bytes calldata _hookData
) external {
    if (!orchestratorRegistry.isOrchestrator(msg.sender)) {
        revert UnauthorizedCaller(msg.sender);
    }
    
    // Pull tokens from orchestrator
    token.safeTransferFrom(msg.sender, address(this), _ctx.executableAmount);
    
    // Execute hook logic
    // ...
}

Build docs developers (and LLMs) love