Skip to content

HookName ​

Purpose: an optional display/targeting name attached to one specific installed hook (an sfHook entry in ltHOOK), which lets any later transaction — not just SetHook — select that one hook out of a multi-hook chain to guarantee it executes (and is paid for), regardless of its HookOn value.

Storage — ltHOOK only, never ltHOOK_DEFINITION.

The per-account ltHOOK entry does carry it. This makes sense: the same WASM code (one ltHOOK_DEFINITION, shared by hash) can be installed under different names on different accounts, or under different names in the same account's chain.

Amendment gating. featureNamedHooks (Supported::yes, VoteBehavior::DefaultNo) gates it in two different places, with two different rejection codes:

  1. Inside a SetHook's sfHook object — temDISABLED if present without the amendment.
  2. As the common transaction-level field (any transaction type) — temMALFORMED if present without featureHooks or featureNamedHooks in shared preflight.

The same validation also enforces the length/UTF-8 rule below for every transaction carrying sfHookName, not only SetHook.

Validation rules:

Read this carefully: the check is on raw byte length (name.size()), which must be 0 (clears the name) or in [4, 16] bytes. The log message says "8 and 32 hex characters" because Blob/VL fields are transmitted as a hex string in JSON RPC — 4–16 raw bytes is 8–32 hex characters on the wire, so the message and the code agree once you account for the hex encoding. The name must also be valid UTF-8 — a purely random 4-byte blob like DEADBEEF (hex) fails this even though it passes the length check while "41424344" encodes ASCII ABCD.

Runtime semantics — targeting, not filtering. During execution and fee calculation, each sfHook entry with a non-empty HookName becomes a required match against the triggering transaction's own sfHookName.

The continue skips that hook entirely (as if it weren't in the chain) unless the transaction's own sfHookName matches exactly. Un-named hooks in the same chain are unaffected — they still run according to their own HookOn regardless of whether the transaction carries a HookName or what it is.

Verified example: account alice has two hooks installed: hook A named "41424344" and hook B unnamed.

Transaction's HookNameHooks that executeBase fee
(absent)B only19 drops
"41424345" (wrong name)B only19 drops
"41424344" (correct name)A and B28 drops (A's fee is added)

This targeting also affects calculateBaseFee (the extra 9 drops above are hook A's own fee, added only when it is actually going to run) and works identically for weakly-executed (TSH/collect) and callback (cbak) hook chains. The emitted callback transaction itself does not need to carry sfHookName to trigger cbak on the named hook.

Common mistakes:

  • Expecting HookName to filter out unnamed hooks when a transaction specifies a name. It does not — unnamed hooks always run; naming only adds an extra, opt-in gate for the hooks that carry a name.
  • Expecting to read/write HookName on ltHOOK_DEFINITION. It is never stored there — only on the per-account ltHOOK entry (sfHook object).
  • Forgetting the two different rejection codes depending on where the unsupported field appears (temDISABLED inside a SetHook's sfHook object vs. temMALFORMED as a transaction-level field) — do not assume one code covers both paths.
  • Passing a name that fails UTF-8 validation despite being 4–16 bytes long (e.g. arbitrary binary/hex blobs) — validation checks both constraints independently.