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 aTransferTransaction—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:- Protocol dependency: New functionality required network-wide upgrades through HIPs (slow and heavyweight)
- EVM migration: Moving applications to smart contracts sacrificed the performance and cost-efficiency of native HAPI transactions
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 aDELEGATECALLfrom the system contract address0x16dto the hook’s implementing contract. - Privileges
The hook executes with the owner’s privileges. A hook attached to account0.0.123can act as0.0.123when calling Hiero system contracts. When executingSTATICCALL,CREATE, orCREATE2, the sender address is the hook’s owner, not0x16d. - Identity
address(this)→0x16dmsg.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 tomsg.sender.
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
TheHookStoreTransaction 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. TheHookStoreTransaction 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 usingContractCreateTransaction (same as standard smart contracts) to receive a ContractId.
Step 2: Attach the Hook
Attach the hook using:AccountCreateTransactionorContractCreateTransactionfor new entitiesAccountUpdateTransactionorContractUpdateTransactionfor existing entities
- 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)
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 theTransferTransaction—attachment alone does not trigger execution.
When triggered:
- Transaction execution pauses
- Hook logic runs
- Transaction continues only if the hook returns
true
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 baseIHieroHook 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
TheProposedTransfers 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 inEvmHookCall.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 aTransferTransaction invokes multiple hooks, the network executes them in a strict order:
-
All
pre_tx_allowance_hookcalls in the HBAR transfer list, in the order they appear. -
For each token transfer list:
- Fungible tokens: all
pre_tx_allowance_hookcalls in the transfers list, in order. - Non-fungible tokens: all
pre_tx_sender_allowance_hookandpre_tx_receiver_allowance_hookcalls in the NFT transfers list, in order. When both sender and receiver hooks exist on the same NFT transfer, the sender hook executes first.
- Fungible tokens: all
-
All
pre_post_tx_allowance_hookcalls (theallowPrepart) in the HBAR transfer list, in order. -
For each token transfer list: all
pre_post_tx_allowance_hookcalls (theallowPrepart), following the same fungible/NFT ordering as step 2. - Main transfer logic executes.
-
All
pre_posthooks from steps 3 and 4, in the same order they were previously executed, now calling theallowPostmethod.
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:CALLCODEalways fails withINVALID_OPERATIONDELEGATECALLalways fails withINVALID_OPERATIONunless done in the facade contract of a native entitySELFDESTRUCTalways fails withINVALID_OPERATIONif done in the frame with address0x16d
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:- The number of hooks the entity has
- The total number of storage slots used by the entity’s EVM hooks
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 byhook.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 bykeyandtimestamp.
Next Steps
- Create and Manage Hooks — Utility class reference for
HookCreationDetails,EvmHook,HookId, and storage update types - Create an account —
addHook()method and examples for creating accounts with hooks - Update an account —
addHookToCreate()/addHookToDelete()methods and examples - Transfer cryptocurrency —
addHbarTransferWithHook(),addTokenTransferWithHook(),addNftTransferWithHook()methods and examples - HookStoreTransaction — SDK reference for hook storage updates
- HIP-1195 — Full technical specification