There is huff-rs and huff-neo?
Yes, there are two versions of the Huff compiler. The original compiler, huff-rs is no longer maintained, and its planned successor huff2 has been discontinued. huff-neo tries to step-in and continues the development and also tries to evolve the Huff language where necessary.
New in huff-neo
There are some new features in huff-neo that are not available in huffc.
Allowed use of built-in functions for constants and code tables
The usage of built-in functions for constants and code tables is now possible.
#define constant FUNC_TEST = __FUNC_SIG("test(uint256)")
#define constant BYTES_HELLO = __BYTES("hello")
#define code_table TEST_TABLE {
0x123
__FUNC_SIG("test(uint256)")
__BYTES("hello")
__LEFTPAD(__FUNC_SIG("test(uint256)")) // New built-in function __LEFTPAD
[FUNC_TEST]
[BYTES_HELLO]
}
Arithmetic expressions in constants
Constants can use arithmetic expressions evaluated at compile time.
#define constant BASE = 0x20
#define constant OFFSET = 0x04
#define constant COMBINED = [BASE] + [OFFSET] // 0x24
#define constant WORD_SIZE = 0x20
#define constant HALF_WORD = [WORD_SIZE] / 0x02 // 0x10
#define constant COMPLEX = (0x0a + 0x05) * 0x02 // 0x1e
Supported operators: +, -, *, /, % (binary), - (unary). Parentheses () for grouping. See the Constants documentation for details.
New builtin constant: __NOOP
A special constant that generates no bytecode, useful for optional macro arguments and conditional compilation patterns.
// Macro with optional operation
#define macro EXECUTE_OPTIONAL(op) = takes(0) returns(0) {
<op> // Can be __NOOP to skip this operation
0x01 0x02 add
}
// With operation
EXECUTE_OPTIONAL(pop) // Generates: pop, push 0x01, push 0x02, add
// Without operation (generates no extra bytecode)
EXECUTE_OPTIONAL(__NOOP) // Generates: push 0x01, push 0x02, add
See the Builtin Constants documentation for details.
New builtin functions
There are new builtin functions available in huff-neo.
__LEFTPAD- Left pads a hex input or the result of a passed builtin in a constant or a code table.__BYTES- Converts a string to the UTF-8 representation bytes and pushes it to the stack.__ASSERT_PC- Validates bytecode position at compile-time to ensure instructions are at expected offsets.__argsstart- Pushes the offset of the ABI-encoded constructor arguments, so a constructor reads them as Solidity does. It replaces__CODECOPY_DYN_ARGofhuff-rs.
__BYTES() example
#define macro MAIN() = takes (0) returns (0) {
__BYTES("hello") // Will push UTF-8 encoded string (PUSH5 0x68656c6c6f)
}
__ASSERT_PC() example
Validates that bytecode is positioned at expected offsets, useful for ensuring jump destinations align correctly. Can be combined with for-loops and constants for precise bytecode positioning.
#define constant TARGET_OFFSET = 0x20
#define macro MAIN() = takes(0) returns(0) {
__ASSERT_PC(0x00) // Assert we're at the start
// Fill space to reach target offset using for-loop with constant
for(i in 0..[TARGET_OFFSET]) {
stop // Expands to 32 stop instructions = 32 bytes
}
__ASSERT_PC([TARGET_OFFSET]) // Assert we're at byte 32
target: // Label for jump destination
}
See the Builtin Functions documentation for complete details.
Constructor arguments and tuple types
-i and -n append the constructor arguments as Solidity’s abi.encode does, typed by
#define function constructor(...), so the same bytecode works with arguments appended by Foundry or cast.
Function, event and error definitions take tuple types such as (address maker, uint256 amount), and -g
declares them as structs.
First-class macros
Macros can now be passed as arguments to other macros and invoked dynamically, making code more reusable and flexible.
#define macro ADD(a, b) = takes(0) returns(1) {
<a> <b> add
}
#define macro APPLY(fn, x, y) = takes(0) returns(1) {
<fn>(<x>, <y>) // Invoke macro with arguments
}
#define macro MAIN() = takes(0) returns(0) {
APPLY(ADD, 0x05, 0x06) // Expands to: 0x05 0x06 add
}
See the Macros and Functions documentation for more details.
Compile-time for loops
Generate repetitive code patterns at compile-time using for loops that expand before bytecode generation.
#define constant COUNT = 10
#define macro INIT_STORAGE() = takes(0) returns(0) {
// Initialize storage slots 0 through 9 with zero
for(i in 0..[COUNT]) {
0x00 <i> sstore
}
}
#define macro PUSH_SEQUENCE() = takes(0) returns(5) {
// Generate: 0x00 0x02 0x04 0x06 0x08
for(i in 0..10 step 2) {
<i>
}
}
See the Compile-Time Loops documentation for complete details and examples.
Compile-time if/else statements
Generate conditional code at compile-time using if/else/else if statements that expand before bytecode generation. Only the matching branch is compiled into the final bytecode.
#define constant MODE = 0x01
#define constant DEBUG = 0x00
#define macro MAIN() = takes(0) returns(0) {
if ([MODE] == 0x01) {
// This code is compiled (condition is true)
0xAA 0x00 mstore
} else if ([MODE] == 0x02) {
// Not compiled
0xBB 0x00 mstore
} else {
// Not compiled
0xCC 0x00 mstore
}
// Supports logical NOT
if (![DEBUG]) {
// This code is compiled (DEBUG is 0, !0 = 1)
0xFF 0x00 mstore
}
}
Supported features:
- Comparison operators:
==,!=,<,>,<=,>= - Logical NOT:
! - Arithmetic expressions:
([A] + [B]) > [C] - Nested if statements
- If/else if/else chains
See the Compile-Time Conditionals documentation for complete details and examples.
New test capabilities
hnc test runs tests on Foundry’s EVM executor, the same engine as forge test. Tests can use cheatcodes, fork a chain with --rpc-url, and print logs and traces with the verbosity levels of forge test. See Tests.