# Transfer Hooks

Token-2022 Transfer Hooks allow a program to execute custom logic on every token transfer. Rome enables Solidity smart contracts to act as transfer hooks — bringing EVM programmability to Solana's token standard.

## How Transfer Hooks Work

Token-2022 (Solana's next-generation token program) supports an extension called Transfer Hook. When a mint has a transfer hook configured:

1. Every `transfer_checked` call for that mint invokes the designated hook program
2. The hook receives transfer details (sender, recipient, amount, mint)
3. The hook can approve or reject the transfer
4. If the hook rejects (reverts), the entire transfer fails

## EVM-Powered Transfer Hooks

On Rome, a Solidity contract can serve as a transfer hook handler:

```
User swaps token on Jupiter (Solana)
    ↓
Jupiter calls transfer_checked
    ↓
Token-2022 invokes the designated hook program
    ↓
Hook program = Rome Meta-Hook Router
    ↓
Router dispatches to Solidity contract via CPI → Rome EVM
    ↓
Solidity contract executes compliance logic
    ↓
Pass: transfer completes
Fail: entire transfer reverts
```

This means **every SPL token transfer on Solana** — whether on Jupiter, Raydium, Phantom, or any wallet — can trigger EVM compliance logic.

## Example: KYC Compliance Hook

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

contract ComplianceHook {
    mapping(address => bool) public kycApproved;
    mapping(address => bool) public sanctioned;

    // Called by Meta-Hook Router on every transfer
    function onTransfer(
        address sender,
        address recipient,
        uint256 amount,
        bytes32 mint
    ) external view {
        // Check sender is KYC'd
        require(kycApproved[sender], "Sender not KYC approved");

        // Check recipient is KYC'd
        require(kycApproved[recipient], "Recipient not KYC approved");

        // Check neither party is sanctioned
        require(!sanctioned[sender], "Sender sanctioned");
        require(!sanctioned[recipient], "Recipient sanctioned");

        // Transfer approved — function returns without revert
    }
}
```

## Key Constraints

**`transfer_checked` only.** Hooks only fire on `transfer_checked` calls, not plain `transfer`. Any integration using Rome tokens must use `transfer_checked` to ensure compliance enforcement.

**Single-state mode required.** Transfer hooks execute inside Solana transactions. OP-Geth is unreachable from that context. All EVM hook logic must run in single-state (proxy) mode.

**CPI depth budget.** The hook invocation consumes CPI depth:

```
Level 0: DeFi protocol → SPL Token-2022
Level 1: Token-2022 → Meta-Hook Router
Level 2: Meta-Hook Router → Rome EVM (CPI precompile)
Level 3: Rome EVM → (any further CPI from Solidity)
```

Only one CPI level remains after the hook invocation chain.

**Compute budget.** EVM hooks consume significant CU:

* Base transfer overhead: 100,000 CU
* Per EVM sub-hook: 200,000 CU
* Recommended budget for EVM transfer: 800,000 CU

## DeFi Protocol Whitelisting

DeFi protocol vaults (Jupiter, Kamino, Orca, Rome bridge vault) need special handling. These vaults receive and send tokens as part of normal operations — blocking them would break DeFi.

The compliance contract maintains a `protocolWhitelist` mapping. Whitelisted addresses (vaults, PDAs for known protocols) are approved without KYC checks. This allows token transfers through DeFi protocols while still enforcing compliance on end-user transfers.

## Address Model

Transfer hooks see **Rome-derived EVM addresses**, not Ethereum addresses. When a Solana user interacts with a Rome-hooked token, their Solana pubkey is mapped to an EVM address via PDA derivation. The Rome Solidity SDK provides utilities for this mapping.

## Related Pages

* [Meta-Hook Router](/products/meta-hook-router.md) — the hook multiplexer that enables multiple sub-hooks per mint
* [Token Interop](/core-concepts/token-interop.md) — how ERC-20 and SPL tokens relate
* [Build Transfer Hook Guide](https://github.com/rome-protocol/docs/blob/main/developer-guides/build-transfer-hook.md) — step-by-step hook development


---

# 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.rome.builders/core-concepts/transfer-hooks.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.
