Skip to main content
Hiero hooks provide programmable extension points to inject Solidity-based logic directly into the network’s transaction pipeline. Hooks attach to accounts to enforce custom rules on actions like token transfers, but they do not run automatically—a hook is triggered only when explicitly referenced in a TransferTransaction (e.g., CryptoTransfer). Unlike regular smart contracts, hooks execute in a special EVM context where address(this) is always the reserved system address 0x16d, enabling them to act with the privileges of the account they’re attached to. This model combines smart contract flexibility with native HAPI transaction efficiency, allowing custom validation without deploying full-scale contracts.

Core Concepts

Hooks are a mechanism for Account Abstraction on Hedera, enabling custom validation and logic without migrating entire applications to the EVM. A hook is a small piece of Solidity logic that is triggered only when referenced/specified in a TransferTransaction—not automatically. Think of it like a webhook for the ledger itself. Instead of waiting for an off-chain call, the hook runs inside the network when a transaction explicitly references it. Hooks can check conditions before execution, update state, log data, or stop a transfer if validation fails.

Why Hooks?

Before Hooks, developers faced two major limitations:
  1. Protocol dependency: New functionality required network-wide upgrades through HIPs (slow and heavyweight)
  2. EVM migration: Moving applications to smart contracts sacrificed the performance and cost-efficiency of native HAPI transactions
Hooks solve this by allowing developers to inject custom logic directly into native flows, offering better performance and lower cost than general-purpose ContractCall operations.

Key Characteristics


How Hooks Work

A Hook is a small piece of EVM bytecode, implemented as a Solidity contract, that is attached to a Hedera entity such as an account or contract.

Special EVM Context (0x16d)

Hooks do not execute at their deployed contract address. Instead, they run inside Hedera’s execution layer where the contract address is always the reserved system address 0x16d. Key properties:
  • Execution
    The network invokes the hook via a DELEGATECALL from the system contract address 0x16d to the hook’s implementing contract.
  • Privileges
    The hook executes with the owner’s privileges. A hook attached to account 0.0.123 can act as 0.0.123 when calling Hiero system contracts. When executing STATICCALL, CREATE, or CREATE2, the sender address is the hook’s owner, not 0x16d.
  • Identity
    • address(this)0x16d
    • msg.sender → transaction payer
  • Storage
    The storage used during hook execution is the hook’s own storage (keyed by entity ID + hook ID), isolated from the implementing contract’s storage.
  • Balance
    If the owning entity has a balance, the hook can use it to transfer value — for example, to refund gas fees to msg.sender.
This is Hedera’s implementation of account abstraction.

Hook Management and State

Hooks are managed via standard HAPI transactions and can maintain their own persistent state.

Hook Management Transactions

For the full utility class reference (HookCreationDetails, EvmHook, HookId, EvmHookStorageUpdate, etc.), see Create and Manage Hooks.

HookStoreTransaction

The HookStoreTransaction enables fast, low-overhead updates to a hook’s storage. This allows hook configuration changes such as passcodes or allowlists without the cost of a full ContractCall.

HookStoreTransaction Properties


Hook Storage Details

Hooks can maintain state in their own storage. The HookStoreTransaction allows for granular updates to this storage without executing the hook’s full logic.
  • Slots: Write or delete a 32-byte key → 32-byte value via EvmHookStorageSlot.
  • Mappings: Update entries under a mapping slot via EvmHookMappingEntries, either by explicit key or by providing the preimage whose Keccak256 hash yields the key.
Important constraintsWhen deleting a hook, you must first clear all its storage slots. Otherwise, the deletion will fail with the status HOOK_DELETION_REQUIRES_EMPTY_STORAGE. Additionally, an account cannot be deleted if it has any hooks attached — CryptoDelete will fail with TRANSACTION_REQUIRES_ZERO_HOOKS.

Extension Points

Hooks attach to specific extension points in a transaction’s lifecycle. An extension point defines the type of hook allowed for a transaction but doesn’t specify when or why a hook is activated. Currently, the first supported extension point is the Account Allowance Hook (ACCOUNT_ALLOWANCE_HOOK). This hook runs when a TransferTransaction references the hook on a transfer entry, acting as a programmable replacement for traditional ERC-style allowances. Future extension points may include other native transaction types or entity lifecycle events, enabling hooks to validate or augment a wide range of on-chain operations. Users can propose a new extension point through the Hiero HIP process.

Hook Lifecycle

Understanding how hooks are deployed, attached, and executed is essential for using Hooks effectively.

Step 1: Deploy the Hook Contract

Deploy the hook’s EVM bytecode using ContractCreateTransaction (same as standard smart contracts) to receive a ContractId.

Step 2: Attach the Hook

Attach the hook using: You must specify:
  • Extension point (e.g., ACCOUNT_ALLOWANCE_HOOK)
  • Hook ID (arbitrary 64-bit identifier, unique per entity)
  • EVM Hook (the deployed ContractId, plus optional initial storage)
  • Admin Key (optional — allows hook deletion and storage updates)
Each of these SDK pages includes hook-specific methods (addHook, addHookToCreate, addHookToDelete) and code examples in Java, JavaScript, and Go. See Create and Manage Hooks for the full utility class reference.

Step 3: Trigger the Hook

Hooks execute ONLY when explicitly referenced in the TransferTransaction—attachment alone does not trigger execution. When triggered:
  1. Transaction execution pauses
  2. Hook logic runs
  3. Transaction continues only if the hook returns true
If the hook reverts or returns false, the entire transaction fails and all state changes roll back. The Transfer cryptocurrency page documents the three WithHook methods (addHbarTransferWithHook, addTokenTransferWithHook, addNftTransferWithHook), the FungibleHookCall/NftHookCall types, and includes code examples for each transfer type.

Account Allowance Hook Interfaces

Account Allowance Hooks are defined by three Solidity interfaces. The base IHieroHook interface provides the HookContext struct shared by all hooks. The IHieroAccountAllowanceHook interface defines the single-call pre-transfer pattern, and IHieroAccountAllowancePrePostHook defines the two-call pre/post-transfer pattern.

IHieroHook — Base Hook Context

All hooks receive a HookContext struct that provides information about the triggering transaction and the hook’s owning entity.

HookContext Fields


IHieroAccountAllowanceHook — Pre-Transfer Only

Use this interface when your hook only needs to validate transfers before execution. The network calls allow() once, before the CryptoTransfer business logic runs. If the function returns false or reverts, the entire transaction fails.

ProposedTransfers Structure

The ProposedTransfers struct gives the hook full visibility into everything the CryptoTransfer is attempting to do: This lets a hook make decisions based on the complete picture — for example, rejecting a transfer if it would trigger custom fees the hook owner doesn’t want to pay.

IHieroAccountAllowancePrePostHook — Pre and Post-Transfer

Use this interface when your hook needs to validate state both before and after the transfer. The network calls allowPre() before the CryptoTransfer business logic, then calls allowPost() after it completes. Both must return true for the transaction to succeed. This is useful for hooks that need to verify actual state changes (e.g., confirming balances shifted as expected) rather than just validating the proposed transfers.

Choosing Between allow() and allowPre()/allowPost()

The choice is made at the TransferTransaction level — the transaction payer specifies either a pre_tx_allowance_hook or a pre_post_tx_allowance_hook reference for each transfer entry. See the Transfer cryptocurrency SDK page for hook call type details and code examples.

Example: Simple Pre-Transfer Hook

Example: Pre/Post Transfer Hook


Execution, Gas, and Call Order

The execution of hooks is subject to specific rules regarding gas, cost, and ordering.

Gas Payer and Cost Model

The transaction payer prepays gas up to the limit set in EvmHookCall.gasLimit. Hooks have a lower intrinsic gas cost than a standard ContractCall (configurable via hooks.evm.intrinsicGasCost, default 1000). In addition to the gas required to execute the hook’s logic, the base CryptoTransfer fee still applies. The hook receives the gas cost in HookContext.gasCost and can refund some or all of it to msg.sender if desired.

Call Order

When a TransferTransaction invokes multiple hooks, the network executes them in a strict order:
  1. All pre_tx_allowance_hook calls in the HBAR transfer list, in the order they appear.
  2. For each token transfer list:
    • Fungible tokens: all pre_tx_allowance_hook calls in the transfers list, in order.
    • Non-fungible tokens: all pre_tx_sender_allowance_hook and pre_tx_receiver_allowance_hook calls in the NFT transfers list, in order. When both sender and receiver hooks exist on the same NFT transfer, the sender hook executes first.
  3. All pre_post_tx_allowance_hook calls (the allowPre part) in the HBAR transfer list, in order.
  4. For each token transfer list: all pre_post_tx_allowance_hook calls (the allowPre part), following the same fungible/NFT ordering as step 2.
  5. Main transfer logic executes.
  6. All pre_post hooks from steps 3 and 4, in the same order they were previously executed, now calling the allowPost method.

Unsupported Contexts

Hook executions are not supported in batch and scheduled transactions (though hook creations are supported in both). Hook executions also restrict certain EVM opcodes:
  • CALLCODE always fails with INVALID_OPERATION
  • DELEGATECALL always fails with INVALID_OPERATION unless done in the facade contract of a native entity
  • SELFDESTRUCT always fails with INVALID_OPERATION if done in the frame with address 0x16d

Limits

  • Child records: Child records generated by hook calls are capped at 50 per transaction (consensus.handle.maxFollowingRecords=50)

Rent and Storage Costs

An entity’s storage footprint — and therefore its rent — grows with the number of hooks it has and the total storage slots those hooks use. The network tracks two summary values per entity:
  1. The number of hooks the entity has
  2. The total number of storage slots used by the entity’s EVM hooks
Rent scales linearly with both values. While rent is not yet enabled on Hedera mainnet, hooks are designed to integrate seamlessly when it is.

Hooks vs Smart Contracts


Mirror Node REST APIs

Mirror nodes expose hook and storage data via two REST endpoints:
  • Hooks API: GET /api/v1/accounts/{idOrAliasOrEvmAddress}/hooks — Returns all hooks for an account, with filtering by hook.id, pagination, and sort order.
  • Hook Storage API: GET /api/v1/accounts/{idOrAliasOrEvmAddress}/hooks/{hook_id}/storage — Returns the current or historical storage state for a specific hook, with filtering by key and timestamp.
See the HIP-1195 specification for full response formats and query parameters.

Next Steps