@@ -19,11 +19,9 @@ updated: 2025-10-03
1919
2020We propose ** hooks** , programmable Hiero extension points that let users customize the behavior of their entities.
2121In principle, hooks could be programmed in any language, but we begin with ** EVM hooks** . Users program EVM hooks by
22- writing contracts in a language like Solidity that compiles to EVM bytecode. EVM hooks may have optimized variants in
23- the future, but we propose first a full-featured ** lambda** EVM hook. The name is chosen to evoke event-driven code
24- running in the cloud or call other external services. For any given entity, its user can create many hooks for that
25- entity with different 64-bit ** hook ids** . There is no limit on the number of hook ids than an entity can use; but
26- its storage footprint, and hence rent, will increase proportionally.
22+ writing contracts in a language like Solidity that compiles to EVM bytecode. For any given entity, its user can create
23+ many hooks for that entity with different 64-bit ** hook ids** . There is no limit on the number of hook ids than an
24+ entity can use; but its storage footprint, and hence rent, will increase proportionally.
2725
2826As a first Hiero extension point, we propose ** account allowance hooks** . Users can create these hooks on their
2927accounts, and a Hiero API (HAPI) ` CryptoTransfer ` transaction can then reference an allowance hook just as it does an
@@ -33,10 +31,10 @@ If the hook function returns `true`, the network continues executing the transfe
3331transfer. Creating an account hook is analogous to adding a function to a smart contract: That is, the hook executes
3432with the account's privileges when calling Hedera system contracts, just as a smart contract's functions do.
3533
36- Unlike smart contracts, which must encapsulate trust guarantees for multiple parties, lambda hooks belong to a single
34+ Unlike smart contracts, which must encapsulate trust guarantees for multiple parties, EVM hooks belong to a single
3735owner who can directly update their storage via a new ` HookStore ` transaction that acts on EVM storage slots. This
38- permits fast, heap adjustments to a lambda 's behavior with less overhead than a typical ` ConsensusSubmitMessage ` ; and
39- far less overhead than a ` ContractCall ` .
36+ permits fast, direct adjustments to an EVM hook 's behavior with less overhead than a typical ` ConsensusSubmitMessage ` ;
37+ and far less overhead than a ` ContractCall ` .
4038
4139## Motivation
4240
@@ -80,7 +78,7 @@ which are charged purely in gas, hook executions are already "gated" by the fee
8078So it makes sense to reduce the intrinsic gas cost of their execution. We propose adding another network property to give
8179this effect while keeping it customizable for Hiero network operators.
8280```
83- hooks.evm.lambdaIntrinsicGasCost =1000
81+ hooks.evm.intrinsicGasCost =1000
8482```
8583
8684### Rent collection
@@ -93,7 +91,7 @@ At the time of this HIP, rent was not yet enabled on Hedera mainnet, but it is i
9391support seamless extension of rent to hooks, we propose to keep in network state two pieces of summary data for each
9492entity using hooks:
95931 . The number of hooks the entity has; and,
96- 2 . The total number of storage slots used by the entity's lambdas .
94+ 2 . The total number of storage slots used by the entity's EVM hooks .
9795
9896The rent of an entity with ` N ` hooks using ` S ` storage slots will then scale linearly with ` N ` and ` S ` .
9997
@@ -133,18 +131,18 @@ address `0x16d` to the hook's implementing contract. That is, even though the ho
133131with a Hiero contract id ` 0.0.H ` , that bytecode _ executes_ with a contract address of ` 0x16d ` . If it calls another
134132contract and that contract, in turn, calls back to address ` 0x16d ` , then control flow returns to the hook's bytecode.
135133
136- As a concrete example, suppose a user creates a ` ACCOUNT_ALLOWANCE_HOOK ` lambda with hook id ` 1 ` for account ` 0.0.A ` .
134+ As a concrete example, suppose a user creates a ` ACCOUNT_ALLOWANCE_HOOK ` EVM hook with id ` 1 ` for account ` 0.0.A ` .
137135The hook's implementing contract is ` 0.0.H ` with EVM address ` 0xab...cd ` . Now ` 0.0.B ` with EVM address ` 0x01...23 `
138136sends a ` CryptoTransfer ` transaction that references the hook ` 0.0.A/1 ` with gas limit ` 100_000 ` .
139137
140138The network will construct an initial EVM message frame with,
1411391 . ` sender ` address ` 0x01...23 ` ; and,
1421402 . ` receiver ` address ` 0x16d ` ; and,
1431413 . ` contract ` address ` 0xab...cd ` (hence the source of the executing bytecode); and,
144- 4 . Storage of the ` 0.0.A/1 ` lambda ; and,
142+ 4 . Storage of the ` 0.0.A/1 ` hook ; and,
1451435 . Gas remaining of ` 99_000 ` (lower intrinsic gas cost).
146144
147- The lambda can then proceed with arbitrary actions, including calls to other contracts, ` SLOAD ` and ` SSTORE ` operations
145+ The hook can then proceed with arbitrary actions, including calls to other contracts, ` SLOAD ` and ` SSTORE ` operations
148146with its storage, and so on. We expect the most common type of EVM hook contract to implement a single external method
149147that reverts when not executed by the network _ as a hook_ . That is,
150148
@@ -197,13 +195,14 @@ EVM programmability without incurring any special risk.
197195
198196#### Unsupported contexts and EVM operations
199197
200- Hook executions are not supported in Batch and Scheduled transactions, but hook creations are supported in
201- Batch and Scheduled transactions.
198+ Hook _ executions_ are not supported in batch and scheduled transactions; though hook creations are supported in both.
202199
203- Hook executions have additional restrictions on EVM opcodes:
200+ Hook executions have additional restrictions on EVM opcodes, as follows :
204201- ` CALLCODE ` will always fail with halt reason of ` INVALID_OPERATION `
205202- ` DELEGATECALL ` will always fail with halt reason of ` INVALID_OPERATION ` unless its is done in the facade contract of native entity.
206- - ` SELFDESTRUCT ` will always fail with halt reason of ` INVALID_OPERATION ` if it is done in the frame with address` 0x16d `
203+ - ` SELFDESTRUCT ` will always fail with halt reason of ` INVALID_OPERATION ` if it is done in the frame with address ` 0x16d `
204+
205+ These restrictions are consistent with the existing Hedera EVM security model.
207206
208207### Mirror node and block explorer support
209208
@@ -233,7 +232,7 @@ Response format:
233232 "extension_point" : " ACCOUNT_ALLOWANCE_HOOK" ,
234233 "hook_id" : 5 ,
235234 "owner_id" : " 0.0.123" ,
236- "type" : " LAMBDA "
235+ "type" : " EVM_HOOK "
237236 }
238237 ],
239238 "links" : {
@@ -348,35 +347,35 @@ message HookCreationDetails {
348347 * A hook programmed in EVM bytecode that may access state or interact with
349348 * external contracts.
350349 */
351- LambdaEvmHook lambda_evm_hook = 4;
350+ EvmHook evm_hook = 4;
352351 }
353352
354353 /**
355354 * If set, a key that that can be used to remove or replace the hook; or (if
356- * applicable, as with a lambda EVM hook) perform transactions that customize
357- * the hook.
355+ * applicable, as with a EVM hook) perform transactions that customize the hook.
358356 */
359357 proto.Key admin_key = 5;
360358}
361359```
362360
363- The ` LambdaEvmHook ` uses a resuable a ` EvmHookSpec ` message that specifies the source of the hook's EVM
364- bytecode. The ` LambdaEvmHook ` message also includes the initial storage slots for the lambda, if desired.
361+ The ` EvmHook ` uses a resuable a ` EvmHookSpec ` message that specifies the source of the hook's EVM
362+ bytecode (in case we ever introduce more specialized EVM hook variants). The ` EvmHook ` message also
363+ includes the initial storage slots for the EVM hook, if desired.
365364
366365``` protobuf
367366/**
368- * Definition of a lambda EVM hook.
367+ * Definition of a EVM hook.
369368 */
370- message LambdaEvmHook {
369+ message EvmHook {
371370 /**
372371 * The specification for the hook.
373372 */
374373 EvmHookSpec spec = 1;
375374
376375 /**
377- * Initial storage updates for the lambda , if any.
376+ * Initial storage updates for the hook , if any.
378377 */
379- repeated LambdaStorageUpdate storage_updates = 2;
378+ repeated EvmHookStorageUpdate storage_updates = 2;
380379}
381380
382381/**
@@ -395,20 +394,20 @@ message EvmHookSpec {
395394}
396395
397396/**
398- * Specifies a key/value pair in the storage of a lambda , either by the explicit storage
397+ * Specifies a key/value pair in the storage of an EVM hook , either by the explicit storage
399398 * slot contents; or by a combination of a Solidity mapping's slot key and the key into
400399 * that mapping.
401400 */
402- message LambdaStorageUpdate {
401+ message EvmHookStorageUpdate {
403402 oneof update {
404403 /**
405404 * An explicit storage slot update.
406405 */
407- LambdaStorageSlot storage_slot = 1;
406+ EvmHookStorageSlot storage_slot = 1;
408407 /**
409408 * An implicit storage slot update specified as a Solidity mapping entry.
410409 */
411- LambdaMappingEntry mapping_entry = 2;
410+ EvmHookMappingEntry mapping_entry = 2;
412411 }
413412}
414413
@@ -424,7 +423,7 @@ message LambdaStorageUpdate {
424423 * stream consumer following the metaprotocol would have to invert the Keccak256
425424 * hash to determine which mapping entry was being updated, which is not possible.
426425 */
427- message LambdaMappingEntry {
426+ message EvmHookMappingEntry {
428427 /**
429428 * The slot that corresponds to the Solidity mapping itself.
430429 */
@@ -444,9 +443,9 @@ message LambdaMappingEntry {
444443}
445444
446445/**
447- * A slot in the storage of a lambda EVM hook.
446+ * A slot in the storage of an EVM hook.
448447 */
449- message LambdaStorageSlot {
448+ message EvmHookStorageSlot {
450449 /**
451450 * The 32-byte key of the slot; leading zeros may be omitted.
452451 */
@@ -461,24 +460,24 @@ message LambdaStorageSlot {
461460}
462461```
463462
464- After an entity has a lambda , a new ` HookStore ` transaction supports efficiently updating the lambda 's storage. It
465- must be signed by either the entity's controlling key or the admin key set in the lambda 's ` HookCreationDetails ` .
463+ After an entity has an EVM hook , a new ` HookStore ` transaction supports directly updating the hook 's storage. It
464+ must be signed by either the entity's controlling key or the admin key set in the hook 's ` HookCreationDetails ` .
466465``` protobuf
467466/**
468- * Adds or removes key/value pairs in the storage of a lambda .
467+ * Adds or removes key/value pairs in the storage of an EVM hook .
469468 * <p>
470- * Either the lambda owner's key or the lambda 's admin key must sign this transaction.
469+ * Either the EVM hook owner's key or the EVM hook 's admin key must sign this transaction.
471470 */
472471message HookStoreTransactionBody {
473472 /**
474- * The id of the lambda whose storage is being updated.
473+ * The id of the EVM hook whose storage is being updated.
475474 */
476475 CreatedHookId hook_id = 1;
477476
478477 /**
479- * The updates to the storage of the lambda .
478+ * The updates to the storage of the EVM hook .
480479 */
481- repeated LambdaStorageUpdate storage_updates = 2;
480+ repeated EvmHookStorageUpdate storage_updates = 2;
482481}
483482```
484483
@@ -632,9 +631,9 @@ message EvmHookState {
632631 proto.CreatedHookId hook_id = 1;
633632
634633 /**
635- * The type of the EVM hook.
634+ * The type of the hook.
636635 */
637- EvmHookType type = 2;
636+ HookType type = 2;
638637
639638 /**
640639 * The type of the extension point the hook implements.
@@ -647,7 +646,7 @@ message EvmHookState {
647646 proto.ContractID hook_contract_id = 4;
648647
649648 /**
650- * For a lambda , its first storage key.
649+ * If the hook has num_storage_slots > 0 , its first storage key.
651650 */
652651 bytes first_contract_storage_key = 5;
653652
@@ -664,33 +663,34 @@ message EvmHookState {
664663 google.protobuf.Int64Value next_hook_id = 7;
665664
666665 /**
667- * The number of storage slots a lambda is using.
666+ * The number of storage slots the hook is using.
668667 */
669668 uint32 num_storage_slots = 8;
670669}
671670
672671/**
673- * The type of an EVM hook.
672+ * The type of a hook; currently just general-purpose EVM hooks are supported, but
673+ * more types will likely be proposed and added over time.
674674 */
675- enum EvmHookType {
675+ enum HookType {
676676 /**
677- * A lambda EVM hook.
677+ * A general-purpose EVM hook.
678678 */
679- LAMBDA = 0;
679+ EVM_HOOK = 0;
680680}
681681```
682682
683683And its storage is keyed by the following type,
684684``` protobuf
685685/**
686- * The key of a lambda 's storage slot.
686+ * The key of an EVM hook 's storage slot.
687687 *
688- * For each lambda , its EVM storage is a mapping of 256-bit keys (or "words")
688+ * For each EVM hook , its storage is a mapping of 256-bit keys (or "words")
689689 * to 256-bit values.
690690 */
691- message LambdaSlotKey {
691+ message EvmHookSlotKey {
692692 /**
693- * The id of the lambda EVM hook that owns this slot.
693+ * The id of the EVM hook that owns this slot.
694694 */
695695 proto.CreatedHookId hook_id = 1;
696696
@@ -774,7 +774,7 @@ id in a single update transaction. (That is, the `hook_ids_to_delete` list is pr
774774` hook_creation_details ` list.)
775775
776776We extend the ` Account ` message in ` TokenService ` state to include the number of hooks in use by an account or contract,
777- and the total number of storage slots their lambdas use; as well as the id of the last hook in the doubly-linked list of
777+ and the total number of storage slots their EVM hooks use; as well as the id of the last hook in the doubly-linked list of
778778the account or contract's hooks.
779779
780780``` protobuf
@@ -793,9 +793,9 @@ message Account {
793793 int64 first_hook_id = 37;
794794
795795 /**
796- * The number of storage slots in use by this account's lambdas .
796+ * The number of storage slots in use by this account's EVM hooks .
797797 */
798- uint64 number_lambda_storage_slots = 38;
798+ uint64 number_evm_hook_storage_slots = 38;
799799}
800800```
801801
@@ -1034,7 +1034,7 @@ sending a NFT from the treasury to account `0.0.X`, and publishing a puzzle. The
10341034use passcode that allows the solver to collect the NFT held by ` 0.0.X ` .
10351035
10361036In particular, the project team creates a custom hook on account ` 0.0.X ` with hook id ` 1 ` . It is an account allowance
1037- lambda EVM hook that references the following contract.
1037+ EVM hook that references the following contract.
10381038
10391039``` solidity
10401040import "./IHieroAccountAllowanceHook.sol";
@@ -1046,9 +1046,9 @@ contract OneTimeCodeTransferAllowance is IHieroAccountAllowanceHook {
10461046 /// Allow the proposed transfers if and only if the args are the
10471047 /// ABI encoding of the current one-time use passcode in storage.
10481048 ///
1049- /// NOTE: this lambda 's behavior does not depend on what owning
1049+ /// NOTE: this hook 's behavior does not depend on what owning
10501050 /// address is set in `context.owner`; it depends only the contents
1051- /// of the active lambda 's 0x00 storage slot.
1051+ /// of the active hook 's 0x00 storage slot.
10521052 function allow(
10531053 IHieroHook.HookContext calldata context,
10541054 ProposedTransfers memory proposedTransfers
@@ -1193,10 +1193,10 @@ In progress, please see [here](https://github.com/hiero-ledger/hiero-consensus-n
11931193 Ultimately it seemed better to keep the charging scheme simple and let hooks manage any refunds themselves.
119411945 . We considered not including the ` IHieroAccountAllowancePrePostHook ` option, but this left a gap in support for the
11951195 full range of HTS/ERC-20 integrations that Hedera users will likely be interested in.
1196- 6 . We considered ** pure** EVM hooks with the same conceptual model as lambdas , but with some severe restrictions. The
1197- initial EVM frame would be marked ` static ` , which prohibits all state-changing operations; and we would also disable
1198- the ` PREVRANDAO ` , ` SLOAD ` , and ` CALL ` opcodes. Such a hook could not do anything but apply a pure function to its
1199- input data; hence pure hook calls could be computed asynchronously, and the results used immediately at consensus.
1196+ 6 . We considered ** pure** EVM hooks with the same conceptual model, but with some severe restrictions. The initial
1197+ EVM frame would be marked ` static ` , which prohibits all state-changing operations; and we would also disable the
1198+ ` PREVRANDAO ` , ` SLOAD ` , and ` CALL ` opcodes. Such a hook could not do anything but apply a pure function to its input
1199+ data; hence pure hook calls could be computed asynchronously, and the results used immediately at consensus.
12001200 But in light of upcoming general approaches to optimistic concurrent transaction handling, the value of pure hooks
12011201 did not seem worth the added complexity. Nonetheless, the protobufs here are designed to let us add such EVM
12021202 specializations in the future if the calculus changes.
0 commit comments