Skip to main content

Herd Action Language

Adapters package reusable reads and call construction. Actions package transaction batches. Add them to collections so teams and agents can share, discover, and simulate complete workflows. Herd agent wallets can then propose action transactions to a Safe or another MPC wallet while the wallet keeps its signing and execution policy.
HAL is a typed, JSON-based expression language for blockchain interactions. Its Scheme-inspired syntax lets agents combine contract reads, calldata construction, conditional logic, code, and transaction batches in a format Herd can validate and simulate. Every HAL expression is valid JSON. An array normally represents a call:
Objects evaluate their values recursively. Strings resolve to scoped variables when a binding exists and otherwise remain literal strings.

Building blocks

Actions

Executable workflows with a typed main entry point and one or more ordered transaction batches.

Adapters

Reusable functions for reads, write-call construction, and code-backed computation.

Collections

Share an action with the read and code adapters that help users understand and simulate it.

Code blocks

Typed TypeScript functions for logic that is more practical outside the HAL expression language.

Actions

An action must export a function named main. Its parameters are the user inputs for the entire workflow. The body defines one or more batches with meta.isBatch: true. Each write-function plans one call. A batch controls when those calls are submitted together; it does not silently merge them.

Complete batched action

This action sends a user-supplied amount of USDC on Base to every address in recipients:
Pass list inputs as JSON arrays. Three recipients plan three calls in one batch. An empty list plans no calls. for-each can also wrap its write-function in an if and return null when an element should not produce a transaction. It cannot contain another for-each.

How wallets submit a batch

The action keeps one planned row per write-function even when a Safe or atomic wallet combines them into one onchain transaction.

Conditional writes

Read current state before changing it. An if can return null when no write is needed:
This pattern avoids replacing a larger approval with a smaller one and prevents unchanged state from producing unnecessary transactions.

Adapters

Adapters package logic that other adapters and actions can import. An adapter exports exactly one function. Use an adapter for:
  • A reusable contract read
  • A reusable write-call payload
  • Typed computation from a code block
Start with logic inside an action. Extract an adapter when the same behavior is useful in several workflows.

Write adapter

A write adapter returns a typed call payload. The action passes that payload to write-function inside a batch.

Read adapter

This adapter reads an account’s native balance and returns an exact uint256 value in wei:

Code adapter

A code adapter calls a published TypeScript code block and coerces its output to a HAL type:

Imports and composition

Import standard library functions by module:
Import a published adapter or action export by its action and expression IDs:
User-defined functions take positional arguments:
Standard library functions generally take one named-argument object:
Only published versions can be imported by ID. Version pins do not automatically move when the imported adapter publishes a newer version.

Collections

Collections group a complete workflow for discovery and sharing. Add:
  • The executable action
  • Read adapters that show current position or protocol state
  • Code adapters that calculate inputs or expected outcomes
Write adapters stay as implementation dependencies and cannot appear as collection items. Collections make it easier for an agent or teammate to find the action, inspect its supporting context, and simulate it before proposing transactions.

Core syntax

Why ABI values must be quoted

HAL recursively evaluates arrays and object values. Quote ABI arrays and function names so HAL treats them as literal data:
Without quote, a string such as amount or decimals can resolve to a scoped value or function instead of remaining ABI metadata.

Type system

HAL supports:
  • Solidity ABI primitives such as address, bool, bytes32, uint256, and int256
  • string, any, and float64
  • Tuples such as { "balance": "uint256" }
  • Arrays such as ["array", "address"]
  • Fixed arrays such as ["array", "uint256", { "length": 3 }]
  • Unions such as ["union", "uint256", "int256", "float64"]
Contract reads return ABI integers as exact big integers. At API boundaries, transaction amounts use decimal strings so they do not lose precision. Use integer math for raw token amounts. Use fadd, fsub, fmul, and fdiv for prices, ratios, or display values that must preserve fractions. decimals scales a human-readable value by a token’s decimals and fails instead of rounding when the input has too many decimal places.

Standard modules

Action execution

Actions have separate mainnet and simulation tracks on one execution.

Simulate

Simulation runs every batch on a sandbox fork and never broadcasts a transaction. It returns a tevmForkId and simulated transaction hashes.
When iterating on an unpublished expression, pass the expression file instead of an action ID.
status: completed means evaluation did not crash. It does not prove the action called the right contract or changed state as intended.
Verify every simulation:
  1. Check each encoded contract address and four-byte function selector.
  2. Check scaled amounts, including token decimals.
  3. Confirm the planned transaction count matches the inputs.
  4. Confirm each write reports a successful transaction status.
  5. Inspect gas use, calls, logs, transfers, and balance changes.
  6. Treat zero gas with no calls, logs, or transfers as evidence that nothing ran.

Plan and continue

mode: "plan" returns the first batch without running it. Submit those calls through the chosen wallet, then continue the mainnet track with each operation ID paired to its transaction hash. Herd checks the onchain traces, records the results, and plans the next batch. Mainnet and simulation advance independently.

Agent wallet proposals

For an agent-wallet proposal, simulate the action with the destination Safe or MPC wallet as the sender. The proposal uses the simulated calls as evidence. The agent wallet can collect group approvals and propose the calls. The destination wallet’s owners or policy still sign and execute them.
Saved actions and adapters start as drafts. Publish a version before importing it, calling it by ID, or starting an execution by ID.