Skip to content

[Advanced]: Implement HIP-1195 Hiero Hooks #496

@rwalworth

Description

@rwalworth

🧠 Advanced

This issue is well-suited for contributors who are very familiar with the
Hiero Swift SDK and enjoy working with its core abstractions and design patterns.

Advanced Issues often involve:

  • Exploring and shaping SDK architecture
  • Reasoning about trade-offs and long-term impact
  • Working across multiple modules or systems
  • Updating tests, examples, and documentation alongside code

The goal is to support thoughtful, high-impact contributions in a clear
and collaborative way.

🐞 Problem Description

The SDK needs to support hooks, programmable Hiero extension points that let users customize the behavior of their entities. This HIP introduces:

  • EVM hooks: Hooks programmed in EVM bytecode that can access state or interact with external contracts
  • Account allowance hooks: The first extension point, allowing custom allowance logic for transfers

Hooks enable users to customize native entities instead of migrating to EVM smart contracts, preserving performance and simplicity while enabling powerful customization.

Use cases:

  • Custom fee enforcement on token transfers
  • One-time passcode allowances for NFT distribution
  • Receiver signature waivers for specific asset types
  • Complex allowance rules without full smart contract migration

💡 Proposed / Expected Outcome

Implement the full hooks API as specified in HIP-1195, including new types, new transactions, and updates to existing transactions.

New Enums

enum HookExtensionPoint {
    case accountAllowanceHook
}

enum FungibleHookType {
    case preHookSender
    case prePostHookSender
    case preHookReceiver
    case prePostHookReceiver
}

enum NftHookType {
    case preHook
    case prePostHook
}

New Types

HookCreationDetails:

struct HookCreationDetails {
    var extensionPoint: HookExtensionPoint
    var hookId: Int64
    var evmHook: EvmHook
    var adminKey: Key?
}

EvmHook:

struct EvmHook {
    var contractId: ContractId
    var storageUpdates: [EvmHookStorageUpdate]?
}

EvmHookStorageUpdate (abstract with variants):

// Explicit storage slot
struct EvmHookStorageSlot {
    var key: Data   // 32-byte storage slot key
    var value: Data // 32-byte value (empty to delete)
}

// Mapping entries
struct EvmHookMappingEntries {
    var mappingSlot: Data
    var entries: [EvmHookMappingEntry]
}

struct EvmHookMappingEntry {
    var key: Data?      // 32-byte key (one of key or preimage required)
    var preimage: Data? // Bytes that hash to the mapping key
    var value: Data     // 32-byte value (empty to delete)
}

HookCall and variants:

struct HookCall {
    var hookId: Int64
    var evmHookCall: EvmHookCall
}

struct FungibleHookCall {
    var hookId: Int64
    var evmHookCall: EvmHookCall
    var hookType: FungibleHookType
}

struct NftHookCall {
    var hookId: Int64
    var evmHookCall: EvmHookCall
    var hookType: NftHookType
}

struct EvmHookCall {
    var data: Data
    var gasLimit: UInt64
}

HookId and HookEntityId:

struct HookId {
    var entityId: HookEntityId
    var hookId: Int64
}

struct HookEntityId {
    var accountId: AccountId?
    var contractId: ContractId?
}

New Transaction

HookStoreTransaction:

class HookStoreTransaction: Transaction {
    func setHookId(_ hookId: HookId) -> Self
    func getHookId() -> HookId?
    func addStorageUpdate(_ update: EvmHookStorageUpdate) -> Self
    func setStorageUpdates(_ updates: [EvmHookStorageUpdate]) -> Self
    func getStorageUpdates() -> [EvmHookStorageUpdate]
}

Updated Transactions

AccountCreateTransaction:

extension AccountCreateTransaction {
    func addHook(_ hook: HookCreationDetails) -> Self
    func setHooks(_ hooks: [HookCreationDetails]) -> Self
    func getHooks() -> [HookCreationDetails]
}

AccountUpdateTransaction:

extension AccountUpdateTransaction {
    func addHookToCreate(_ hook: HookCreationDetails) -> Self
    func setHooksToCreate(_ hooks: [HookCreationDetails]) -> Self
    func addHookToDelete(_ hookId: Int64) -> Self
    func setHooksToDelete(_ hookIds: [Int64]) -> Self
    func getHooksToCreate() -> [HookCreationDetails]
    func getHooksToDelete() -> [Int64]
}

ContractCreateTransaction:

extension ContractCreateTransaction {
    func addHook(_ hook: HookCreationDetails) -> Self
    func setHooks(_ hooks: [HookCreationDetails]) -> Self
    func getHooks() -> [HookCreationDetails]
}

ContractUpdateTransaction:

extension ContractUpdateTransaction {
    func addHookToCreate(_ hook: HookCreationDetails) -> Self
    func setHooksToCreate(_ hooks: [HookCreationDetails]) -> Self
    func addHookToDelete(_ hookId: Int64) -> Self
    func setHooksToDelete(_ hookIds: [Int64]) -> Self
    func getHooksToCreate() -> [HookCreationDetails]
    func getHooksToDelete() -> [Int64]
}

TransferTransaction:

extension TransferTransaction {
    func addHbarTransferWithHook(_ accountId: AccountId, _ amount: Hbar, _ hookCall: FungibleHookCall) -> Self
    func addNftTransferWithHook(_ nftId: NftId, _ sender: AccountId, _ receiver: AccountId, 
                                 _ senderHookCall: NftHookCall?, _ receiverHookCall: NftHookCall?) -> Self
    func addTokenTransferWithHook(_ tokenId: TokenId, _ accountId: AccountId, _ amount: Int64, 
                                   _ hookCall: FungibleHookCall) -> Self
}

🧠 Implementation & Design Notes

Implementation Phases

Phase 1: Core Types

  • Implement all enums (HookExtensionPoint, FungibleHookType, NftHookType)
  • Implement utility types (EvmHook, EvmHookStorageUpdate variants, HookCreationDetails)
  • Implement hook call types (HookCall, FungibleHookCall, NftHookCall, EvmHookCall)
  • Implement identifier types (HookId, HookEntityId)

Phase 2: HookStoreTransaction

  • New transaction for updating EVM hook storage
  • Protobuf serialization/deserialization
  • Unit tests

Phase 3: Account Transactions

  • Update AccountCreateTransaction with hook creation
  • Update AccountUpdateTransaction with hook create/delete
  • Unit and integration tests

Phase 4: Contract Transactions

  • Update ContractCreateTransaction with hook creation
  • Update ContractUpdateTransaction with hook create/delete
  • Unit and integration tests

Phase 5: TransferTransaction

  • Add hook-aware transfer methods
  • Handle FungibleHookCall and NftHookCall in transfers
  • Unit and integration tests

Phase 6: Examples

  • Create examples demonstrating hook creation and usage

Error Codes to Handle

Error Code Description
INVALID_HOOK_CREATION_SPEC Hook creation missing required fields
HOOK_ID_REPEATED_IN_CREATION_DETAILS Duplicate hook IDs in same transaction
HOOK_ID_IN_USE Hook ID already exists on entity
HOOK_NOT_FOUND Referenced hook doesn't exist
HOOK_DELETED Attempting to delete already-deleted hook

✅ Acceptance Criteria

A pull request for this issue should:

  • Implement HookExtensionPoint enum
  • Implement FungibleHookType and NftHookType enums
  • Implement HookCreationDetails struct
  • Implement EvmHook struct
  • Implement EvmHookStorageUpdate variants (StorageSlot, MappingEntries)
  • Implement HookCall, FungibleHookCall, NftHookCall structs
  • Implement EvmHookCall struct
  • Implement HookId and HookEntityId structs
  • Implement HookStoreTransaction
  • Update AccountCreateTransaction with hook methods
  • Update AccountUpdateTransaction with hook create/delete methods
  • Update ContractCreateTransaction with hook methods
  • Update ContractUpdateTransaction with hook create/delete methods
  • Update TransferTransaction with hook-aware transfer methods
  • Add unit tests (38 test cases per test plan)
  • Add integration tests
  • Add examples
  • Follow existing architectural and Swift conventions
  • Pass all CI checks

Test Plan Summary (38 test cases)

AccountCreateTransaction (5 tests):
1-5. Create accounts with hooks, storage updates, admin keys, error cases

AccountUpdateTransaction (9 tests):
6-14. Add/delete hooks, error cases for duplicates, missing hooks

ContractCreateTransaction (5 tests):
15-19. Create contracts with hooks, storage updates, admin keys, error cases

ContractUpdateTransaction (9 tests):
20-28. Add/delete hooks from contracts, error cases

HookStoreTransaction (3 tests):
29-31. Update storage, signature validation, missing hook errors

TransferTransaction (7 tests):
32-38. HBAR/token/NFT transfers with various hook types

📚 Additional Context, Links, or Prior Art

References

Example: Creating an Account with an EVM Hook

import Hiero

// Create initial storage updates
let storageSlot = EvmHookStorageSlot(
    key: Data(repeating: 1, count: 32),
    value: Data(repeating: 100, count: 32)
)

// Create EVM hook with storage
let evmHook = EvmHook(
    contractId: ContractId.fromString("0.0.67890"),
    storageUpdates: [.storageSlot(storageSlot)]
)

let hookDetails = HookCreationDetails(
    extensionPoint: .accountAllowanceHook,
    hookId: 1002,
    evmHook: evmHook,
    adminKey: adminKey.publicKey
)

// Create account with hook
let tx = try AccountCreateTransaction()
    .key(.single(accountKey.publicKey))
    .addHook(hookDetails)
    .freezeWith(client)
    .sign(accountKey)

let response = try await tx.execute(client)
let accountId = try await response.getReceipt(client).accountId!

Example: Transfer with Hook

let hookCall = FungibleHookCall(
    hookId: 1001,
    evmHookCall: EvmHookCall(
        data: Data([0x01, 0x02]),
        gasLimit: 50000
    ),
    hookType: .preHookSender
)

let tx = try TransferTransaction()
    .addHbarTransferWithHook(senderAccountId, Hbar(-100), hookCall)
    .addHbarTransfer(receiverAccountId, Hbar(100))
    .freezeWith(client)
    .sign(senderKey)

try await tx.execute(client)

If you have questions, the community is happy to help:

https://discord.com/channels/905194001349627914/1337424839761465364

Metadata

Metadata

Assignees

Labels

priority: mediumNormal priority; to be addressed in the standard development cyclescope: apiRelated to the public SDK API surfaceskill: advancedRequires deep understanding of the SDK architecture and may span multiple modulesstatus: blockedWork is stalled due to external dependency or required decision

Projects

Status

👀 In Review

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions