Task Hooks
Load this reference when getTaskHooks(taskId) (or the API's ordered hook list) contains one or more addresses, or before constructing a task that should be gated or observed by an external contract.
What A Hook Is
A hook is an external contract implementing ITMPHook (ERC-8195), registered immutably on a task at creation time via repeatable --hook flags. Protocol defaults execute first, then custom hooks in the order supplied. Once set, the list cannot be changed or removed. TaskMarket calls into every hook at defined points in the task lifecycle; hooks can validate or react to those transitions, but never hold escrowed funds themselves.
taskmarket task create \
--description "..." \
--reward <usdc> \
--duration <hours> \
--mode <mode> \
--hook <address> \
--hook <address> \
--hook-data <hex>--hook-data is one opaque configuration value forwarded to checkFund on every hook in that task, including protocol defaults. There is no per-hook hookData value in V1. Encode a uint32 as 4 big-endian bytes, e.g. 0x000006b4 for an 1800-second TWAP window. Combine hooks only when every hook is designed to accept the same bytes (or 0x); a wrong address or configuration cannot be corrected after funding. The Diamond accepts no more than eight default and custom hooks combined.
check* vs on*
ITMPHook splits its calls into two families with different guarantees (packages/contracts/src/interfaces/ITMPHook.sol):
check*(checkFund,checkClaim,checkSelectWorker,checkSubmit,checkEvaluate,checkComplete) -- called after all task state for that transition is committed, but before TaskMarket's outbound payout transfers. Returningfalseor reverting blocks the transition; a rejection reverts all state changes cleanly. Exception:checkFundruns insidecreateTask, where USDC has already moved via the PGTR forwarder before the relayed call arrives -- acheckFundimplementation cannot assume pre-transfer balances.checkEvaluatemay be a no-op if the hook doesn't care about evaluation events.on*(onComplete,onForfeit,onCancel,onExpire) -- called after all state and transfers are committed. Failures are swallowed via try-catch (best-effort): a buggy or maliciouson*implementation cannot block fund recovery. Use these for side effects like minting reward tokens or emitting external notifications.
Side effects inside a hook are permitted; re-entrant calls back into TaskMarket are blocked by nonReentrant.
Worked Example
The DREAMS token reward system is itself a shipped ITMPHook implementation: TaskTokenRewardHook (see DREAMS Token Rewards and the contract address table in Smart Contracts). Its checkComplete may credit DREAMS to the worker and requester, subject to the configured rate, available epoch budget, and vault liquidity; its on* functions only handle defensive cleanup (releasing an unpaid reservation). It is a concrete reference for a production hook. For a full walkthrough of its source aimed at developers writing their own hook, see Building Task Hooks.
Anti-Patterns
- Attaching a hook address without the requester's operator confirming it -- there is no way to detach or replace it later.
- Assuming a
check*hook ran before state changes; it runs after state commit and before transfers. - Relying on an
on*hook's side effect completing -- it is best-effort and its failure is silently swallowed.