This feature is currently under beta. More documentation is coming soon. Reach out to [email protected] for more information.
HAL Expression System Documentation
Overview
HAL (Herd Action Language) is a DSL scripting language in JSON for simplifying writing transactions and reading data from the blockchain. It’s used to create simplified write and read functions as “adapters” that can be reused across each other and also “actions” for transactions.Actions vs Adapters
These are the two types of HAL expressions we let you create in Herd. Both use the same underlying language, and are meant to be composed together through imports/exports. Actions are executable batches of write functions that:- Have a
mainfunction as the entry point - Have batches and steps, where each batch can contain many “step” write function that get their calldata calculated together. They can be signed/sent through 7702, 5792, 4337, or just normal single sign transactions.
- Accept user parameters once, no matter how many steps/batches
- Can be executed in the Herd executor to produce onchain transactions
- Transactions are saved for when you need to reference or audit them
- Transactions can be simulated for security/previews
- Specifically wrap a write function, read function, or code block
- For write functions, it outputs calldata which can be used in action batches for transactions or as encoded calldata for multicall/bytes fields
- For read functions, it returns the read outputs defined in the ABI
- For code blocks, it runs the typescript code to return the code outputs. we use valtown/deno for code blocks. These can be created from the import/create adapter views.
- Adapters can be imported into other adapter or actions for composable re-use.
HAL “Define” and User Parameters
Thedefine statement creates named functions with typed parameters - this is what is exported as the action/adapter. This is the core mechanism for simplifying complex onchain interactions - turning a contract function with 10+ inputs into a user-friendly function with just 2-3 inputs.
Fields:
- name: Function name (used for calls and exports)
- parameters: Array of
{ name, type }objects defining user inputs (can include optionalmetafield for parameter-level metadata) - body: The expression to evaluate when the function is called
- meta (optional): Metadata for UI display and documentation (see Metadata section below)
Metadata
The optionalmeta field on define functions provides UI hints and documentation. Metadata schemas:
Batch metadata (meta on batch define with isBatch: true):
meta on parameter objects):
How Parameters Work Through Functions
When a user calls a defined function, parameters are bound in the evaluation context and can be referenced by name:"to" and "amount" in the body reference the parameter values passed by the user. When you reference another define/import, only its parameters are used. And when evaluating, the parameters top level are passed once and then down through any referenced functions as well.
The Key Value of HAL: Simplifying Complex Contract Calls
A key value of HAL is turning complex contract functions into simple user-facing adapters. For example, a contract’sregister function might require 10 arguments:
Complex contract function (10 inputs):
name,owner,duration,resolver,data[],coinTypes[],reverseRecord,signature,signatureExpiry,expires
Setting values in a HAL expression.
Any variable can be set to a value with hardcoded values or function references (from defines and imports). The most common pattern you will see in HAL is wrapping a function call with agetPath and then a coerce. You can create a parameter and reference it in the value too (if in a define).
"duration"is set by calling the imported functioncalculate_years_to_secondswith the user parameter"years"- The result is then extracted using
getPathto get the"seconds"field from the"duration"object
Standard Library Functions
HAL provides standard library functions imported from theherd and object modules. Import them using:
write-function
Executes a state-changing contract call. Only used within batch steps in themain function of actions.
encodedCalldata: Result fromencode-calldatapayable: booleanpayableAmount: uint256 (in wei)
{ blockchain, toAddress, functionSignature, transactionHash, transactionStatus }
This is the only function that submits/takes a transaction hash!!!
Batch Call Data Format (Use for Write Function adapters)
User-defined write functions return a standardized format for use in action batches:write-function in batch steps. This format does NOT produce a transaction, only the write-function does.
read-function
Executes a read-only contract call (view/pure functions).{ calldata: EncodeCalldataExpression, outputAbi: ["quote", AbiParameter[]] }
Returns: Object with decoded output values keyed by ABI output names (e.g., { balance: "1000000" })
encode-calldata
Encodes function arguments into calldata for contract calls. Used internally by read/write functions. Returns:{ blockchain, contractAddress, functionSignature, calldata }
code
Executes a TypeScript code block. Code blocks are created/managed in the Code Blocks view and run on Valtown/Deno.idformat:definitionId:codeVersionId- combines the definition UUID with the specific code version- Each code block has a single auto-synced adapter that updates when the code is published
- Input/output args are typed using HAL types
getPath
Gets a value at a nested path within an object. Imported from theobject module.
Expression format:
{ object: any, path: string[] }
Returns: Value at the specified path
coerce
Runtime type coercion and validation. Wraps a value to declare its expected type for the type inference system.code, read-function, decimals, and write adapters returning batch call data (see examples above).
decimals
Multiplies a value by 10^decimals. Converts human-readable values to raw integers (e.g., for wei).{ value: uint256|int256|float64, decimals: uint8 }
Returns: String representation of the scaled integer
user-wallet
Returns the current user’s wallet address. No arguments required. Returns:address (e.g., "0x1234...")
lookup-transaction-function
Retrieves function inputs or outputs from a committed transaction trace. Only used in step outputs withinmain to extract values from previous transaction results.
direction:"inputs"or"outputs"abi: Quoted array of ABI parameters for decoding
lookup-transaction-event
Retrieves event outputs from a committed transaction’s logs. Only used in step outputs withinmain to extract event data from previous transactions.
Import/Export and Versioning
Import Syntax
Import functions from other adapters/actions or stdlib modules:action:actionId:versionId
actionId: UUID of the action/adapterversionId: UUID of the specific version to import
Export Syntax
Export functions for use by other expressions:Versioning
Critical: When you publish a new version of an adapter, any actions/adapters that import it will continue using the old version until updated. To update imports:- Importers must explicitly update the
versionIdin their import statement - The HAL editor shows stale import warnings when newer versions are available
- Update the import to use the new version ID:
action:actionId:newVersionId
Evaluation and Operation Logs (Oplogs)
You can test any HAL expression by clicking “Test” mode in the action editor, entering user parameters, and clicking run. The evaluation produces an operation log (oplog) - a detailed trace of every function call during execution.Oplog Structure
Each entry represents a single function call:- operationId: Unique ID for this operation
- functionName: Name of the function called (e.g.,
code,read-function,getPath, user-defined functions) - status:
"success"or"error" - timestamp/timestampIso: When the operation executed
- args: Arguments passed to the function
- result: Return value (or error message if failed)
Example Oplog Walkthrough
Here’s a condensed example showing how operations chain together:codeexecutes a TypeScript code block to convert years to secondscalculate_years_to_secondsis the user-defined wrapper functiongetPathextracts thesecondsfield from the resultencode-calldataprepares the contract call argumentsread-functioncalls the contract to get pricing infosimpleRegisteris the final user-defined adapter that returns batch call data
Using Oplogs for Debugging
- Trace execution flow: See exactly which functions run and in what order and check what each function receives and returns
- Identify failures: Failed operations show
"status": "error"with error messages

