Repository: huff-language/huff-rs (rust)

Issue:
HIP-01: Syntactic sugar for function signatures 
## Simple Summary

Computing function signatures is a bit of a hassle and having them displayed as hardcoded values increases the technical debt.
Some *syntactic sugar* could be added to the language itself (e.g. `FUNC_SIG('set(uint256)')`) that will allow developers to compute function signatures directly within the source code.

## Motivation

Function signatures are currently hardcoded in Huff contracts, which means that `set(uint256)` will be displayed as `0x60fe47b1`. This current solution is not very practical, since developers may have to use an external tool to compute the full keccak-256 hash, trim the extra bytes manually to get the function signature itself, copy / paste it into the source code and eventually add a comment to remember where this value is coming from.

Here is a snippet of the current situation:

```cs
#define macro MAIN() = takes(0) returns (0) {
  0x00 calldataload
  0xe0 shr
  dup1
  0x60fe47b1 // signature of `set(uint256)`
  eq
  set jumpi
  dup1
  0x6d4ce63c // signature of `get()`
  eq
  get jumpi
  set:
    SET()
  get:
    GET()
}
```

## Specification

Via the new `FUNC_SIG` keyword, developers would be able to compute function signatures directly in their Huff contracts.
Here is a proposed implementation:

```cs
#define macro MAIN() = takes(0) returns (0) {
  0x00 calldataload
  0xe0 shr
  dup1
  FUNC_SIG('set(uint256)')
  eq
  set jumpi
  dup1
  FUNC_SIG('get()')
  eq
  get jumpi
  set:
    SET()
  get:
    GET()
}
```

The compiler will interpret the *helper function* `FUNC_SIG` and replace it with the correct function signature. For example:
- `FUNC_SIG('set(uint256)')` will become `0x60fe47b1`
- `FUNC_SIG('get()')` will become `0x6d4ce63c`

## Notes

A similar *helper function* could be added to get the hash of events: `EVENT_HASH('...')`.

Pull request description:
feat(huff_codegen): Builtin functions for event and function signatures
## Overview

Adds two new builtin functions to assist in pushing function and event signatures to the stack.
* `__FUNC_SIG(functionName)`
* `__EVENT_HASH(eventName)`

Example:
```
#define function transfer(address,uint256) nonpayable returns ()

#define macro TRANSFER() = takes (0) returns (0) {
    // ...
}

#define macro MAIN() = takes(0) returns (0) {
    // Identify which function is being called.
    0x00 calldataload 0xE0 shr
    dup1 __FUNC_SIG(transfer) /* 0xa9059cbb */ eq transfer jumpi
    
    transfer:
        TRANSFER()
}
```

```
#define event transfer(address,address,uint256)

#define macro CONSTRUCTOR() = takes(0) returns (0) {
    __EVENT_HASH(transfer) // 0xbeabacc8ffedac16e9a60acdb2ca743d80c2ebb44977a93fa8e483c74d2b35a8
    0x00 sstore
}
```

Proposed by @clemlak
resolves #149

## Checklist
- [x] Determine syntax (currently `__FUNC_SIG(functionName)` and `__EVENT_HASH(eventName)`)
- [x] Do we want to accept function definitions as the parameter, or strings? (currently function/event definitions)
  - Decided to do both 

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index ee78d1b..2be1f60 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -673,6 +673,7 @@ dependencies = [
  "huff_utils",
  "proptest",
  "serde_json",
+ "tiny-keccak",
  "tracing",
  "uuid",
 ]
diff --git a/huff_codegen/Cargo.toml b/huff_codegen/Cargo.toml
index 0bc30f3..67dab54 100644
--- a/huff_codegen/Cargo.toml
+++ b/huff_codegen/Cargo.toml
@@ -19,3 +19,4 @@ ethers-core = "0.13.0"
 hex = "0.4.3"
 tracing = "0.1.34"
 uuid = { version = "1.1.1", features = ["v4"] }
+tiny-keccak = { version = "2.0.2", features = ["keccak"] }
diff --git a/huff_codegen/src/irgen/statements.rs b/huff_codegen/src/irgen/statements.rs
index 4c6e432..7a26477 100644
--- a/huff_codegen/src/irgen/statements.rs
+++ b/huff_codegen/src/irgen/statements.rs
@@ -1,4 +1,5 @@
 use huff_utils::prelude::*;
+use tiny_keccak::{Hasher, Keccak};
 
 use crate::Codegen;
 
@@ -118,7 +119,7 @@ pub fn statement_gen(
                             kind: CodegenErrorKind::MissingMacroDefinition(
                                 bf.args[0].name.as_ref().unwrap().to_string(), /* yuck */
                             ),
-                            span: AstSpan(vec![Span { start: 0, end: 0, file: None }]),
+                            span: bf.span.clone(),
                             token: None,
                         })
                     };
@@ -165,7 +166,7 @@ pub fn statement_gen(
                             kind: CodegenErrorKind::MissingMacroDefinition(
                                 bf.args[0].name.as_ref().unwrap().to_string(), /* yuck */
                             ),
-                            span: AstSpan(vec![Span { start: 0, end: 0, file: None }]),
+                            span: bf.span.clone(),
                             token: None,
                         })
                     };
@@ -186,6 +187,112 @@ pub fn statement_gen(
                     bytes.push((*offset, Bytes(format!("{}xxxx", Opcode::Push2))));
                     *offset += 3;
                 }
+                BuiltinFunctionKind::FunctionSignature => {
+                    if bf.args.len() != 1 {
+                        tracing::error!(
+                            target: "codegen",
+                            "Incorrect number of arguments passed to __FUNC_SIG, should be 1: {}",
+                            bf.args.len()
+                        );
+                        return Err(CodegenError {
+                            kind: CodegenErrorKind::InvalidArguments(
+                                format!(
+                                    "Incorrect number of arguments passed to __FUNC_SIG, should be 1: {}",
+                                    bf.args.len()
+                                )
+                            ),
+                            span: bf.span.clone(),
+                            token: None,
+                        })
+                    }
+
+                    if let Some(func) = contract
+                        .functions
+                        .iter()
+                        .find(|f| bf.args[0].name.as_ref().unwrap().eq(&f.name))
+                    {
+                        let sig = hex::encode(func.signature);
+                        let push_bytes = format!("{:02x}{}", 95 + sig.len() / 2, sig);
+                        *offset += push_bytes.len() / 2;
+                        bytes.push((starting_offset, Bytes(push_bytes)));
+                    } else if let Some(s) = &bf.args[0].name {
+                        let mut signature = [0u8; 4]; // Only keep first 4 bytes
+                        let mut hasher = Keccak::v256();
+                        hasher.update(s.as_bytes());
+                        hasher.finalize(&mut signature);
+
+                        let sig = hex::encode(signature);
+                        let push_bytes = format!("{:02x}{}", 95 + sig.len() / 2, sig);
+                        *offset += push_bytes.len() / 2;
+                        bytes.push((starting_offset, Bytes(push_bytes)));
+                    } else {
+                        tracing::error!(
+                            target: "codegen",
+                            "MISSING FUNCTION INTERFACE PASSED TO __SIG: \"{}\"",
+                            bf.args[0].name.as_ref().unwrap()
+                        );
+                        return Err(CodegenError {
+                            kind: CodegenErrorKind::MissingFunctionInterface(
+                                bf.args[0].name.as_ref().unwrap().to_string(),
+                            ),
+                            span: bf.span.clone(),
+                            token: None,
+                        })
+                    }
+                }
+                BuiltinFunctionKind::EventHash => {
+                    if bf.args.len() != 1 {
+                        tracing::error!(
+                            target: "codegen",
+                            "Incorrect number of arguments passed to __EVENT_HASH, should be 1: {}",
+                            bf.args.len()
+                        );
+                        return Err(CodegenError {
+                            kind: CodegenErrorKind::InvalidArguments(
+                                format!(
+                                    "Incorrect number of arguments passed to __EVENT_HASH, should be 1: {}",
+                                    bf.args.len()
+                                )
+                            ),
+                            span: bf.span.clone(),
+                            token: None,
+                        })
+                    }
+
+                    if let Some(event) = contract
+                        .events
+                        .iter()
+                        .find(|e| bf.args[0].name.as_ref().unwrap().eq(&e.name))
+                    {
+                        let hash = bytes32_to_string(&event.hash, false);
+                        let push_bytes = format!("{:02x}{}", 95 + hash.len() / 2, hash);
+                        *offset += push_bytes.len() / 2;
+                        bytes.push((starting_offset, Bytes(push_bytes)));
+                    } else if let Some(s) = &bf.args[0].name {
+                        let mut hash = [0u8; 32];
+                        let mut hasher = Keccak::v256();
+                        hasher.update(s.as_bytes());
+                        hasher.finalize(&mut hash);
+
+                        let hash = hex::encode(hash);
+                        let push_bytes = format!("{:02x}{}", 95 + hash.len() / 2, hash);
+                        *offset += push_bytes.len() / 2;
+                        bytes.push((starting_offset, Bytes(push_bytes)));
+                    } else {
+                        tracing::error!(
+                            target: "codegen",
+                            "MISSING EVENT INTERFACE PASSED TO __EVENT_HASH: \"{}\"",
+                            bf.args[0].name.as_ref().unwrap()
+                        );
+                        return Err(CodegenError {
+                            kind: CodegenErrorKind::MissingEventInterface(
+                                bf.args[0].name.as_ref().unwrap().to_string(),
+                            ),
+                            span: bf.span.clone(),
+                            token: None,
+                        })
+                    }
+                }
             }
         }
         sty => {
diff --git a/huff_lexer/src/lib.rs b/huff_lexer/src/lib.rs
index f2f19cc..87f86e4 100644
--- a/huff_lexer/src/lib.rs
+++ b/huff_lexer/src/lib.rs
@@ -552,7 +552,11 @@ impl<'a> Iterator for Lexer<'a> {
                         if self.context == Context::MacroBody &&
                             matches!(
                                 slice.as_ref(),
-                                "__codesize" | "__tablesize" | "__tablestart"
+                                "__codesize" |
+                                    "__tablesize" |
+                                    "__tablestart" |
+                                    "__FUNC_SIG" |
+                                    "__EVENT_HASH" /* TODO: Clean this process up */
                             )
                         {
                             TokenKind::BuiltinFunction(slice)
diff --git a/huff_parser/src/lib.rs b/huff_parser/src/lib.rs
index 28e8175..96e4325 100644
--- a/huff_parser/src/lib.rs
+++ b/huff_parser/src/lib.rs
@@ -242,7 +242,7 @@ impl Parser {
         };
 
         // function inputs should be next
-        let inputs: Vec<Argument> = self.parse_args(true, true, false)?;
+        let inputs: Vec<Argument> = self.parse_args(true, true, false, false)?;
         // function type should be next
         let fn_type = match self.current_token.kind.clone() {
             TokenKind::View => FunctionType::View,
@@ -265,7 +265,7 @@ impl Parser {
         // next token should be of `TokenKind::Returns`
         self.match_kind(TokenKind::Returns)?;
         // function outputs should be next
-        let outputs: Vec<Argument> = self.parse_args(true, true, false)?;
+        let outputs: Vec<Argument> = self.parse_args(true, true, false, false)?;
 
         let mut signature = [0u8; 4]; // Only keep first 4 bytes
         let mut hasher = Keccak::v256();
@@ -306,9 +306,16 @@ impl Parser {
         };
 
         // Parse the event's parameters
-        let parameters: Vec<Argument> = self.parse_args(true, true, true)?;
+        let parameters: Vec<Argument> = self.parse_args(true, true, true, false)?;
 
-        Ok(Event { name, parameters, span: AstSpan(self.spans.clone()) })
+        let mut hash = [0u8; 32];
+        let mut hasher = Keccak::v256();
+        let input_types =
+            parameters.iter().map(|i| i.arg_type.as_ref().unwrap().clone()).collect::<Vec<_>>();
+        hasher.update(format!("{}({})", name, input_types.join(",")).as_bytes());
+        hasher.finalize(&mut hash);
+
+        Ok(Event { name, parameters, span: AstSpan(self.spans.clone()), hash })
     }
 
     /// Parse a constant.
@@ -375,7 +382,7 @@ impl Parser {
             self.match_kind(TokenKind::Ident("MACRO_NAME".to_string()))?.to_string();
         tracing::info!(target: "parser", "PARSING MACRO: \"{}\"", macro_name);
 
-        let macro_arguments: Vec<Argument> = self.parse_args(true, false, false)?;
+        let macro_arguments: Vec<Argument> = self.parse_args(true, false, false, false)?;
         self.match_kind(TokenKind::Assign)?;
         self.match_kind(TokenKind::Takes)?;
         let macro_takes: usize = self.parse_single_arg()?;
@@ -485,7 +492,7 @@ impl Parser {
                 TokenKind::BuiltinFunction(f) => {
                     let mut curr_spans = vec![self.current_token.span.clone()];
                     self.match_kind(TokenKind::BuiltinFunction(String::default()))?;
-                    let args = self.parse_args(true, false, false)?;
+                    let args = self.parse_args(true, false, false, true)?;
                     args.iter().for_each(|a| curr_spans.extend_from_slice(&a.span.0));
                     tracing::info!(target: "parser", "PARSING MACRO BODY: [BUILTIN FN: {}({:?})]", f, args);
                     statements.push(Statement {
@@ -629,10 +636,33 @@ impl Parser {
         select_name: bool,
         select_type: bool,
         has_indexed: bool,
+        is_builtin: bool,
     ) -> Result<Vec<Argument>, ParserError> {
         let mut args: Vec<Argument> = Vec::new();
         self.match_kind(TokenKind::OpenParen)?;
         while !self.check(TokenKind::CloseParen) {
+            // The builtin functions `__FUNC_SIG` and `__EVENT_HASH` can accept a single string as
+            // input. If the `is_builtin` flag was passed, check to see if a single
+            // string is present.
+            if let TokenKind::Str(s) = &self.current_token.kind {
+                if !is_builtin {
+                    return Err(ParserError {
+                        kind: ParserErrorKind::InvalidArgs(self.current_token.kind.clone()),
+                        hint: None,
+                        spans: AstSpan(vec![self.current_token.span.clone()]),
+                    })
+                }
+
+                args.push(Argument {
+                    name: Some(s.to_owned()), // Place the string in the "name" field
+                    arg_type: None,
+                    indexed: false,
+                    span: AstSpan(vec![self.current_token.span.clone()]),
+                });
+                self.consume();
+                continue
+            }
+
             let mut arg = Argument::default();
             let mut arg_spans = vec![];
 
diff --git a/huff_utils/src/ast.rs b/huff_utils/src/ast.rs
index 2a3899d..841310e 100644
--- a/huff_utils/src/ast.rs
+++ b/huff_utils/src/ast.rs
@@ -345,6 +345,8 @@ pub struct Event {
     pub parameters: Vec<Argument>,
     /// The event span
     pub span: AstSpan,
+    /// The event hash
+    pub hash: Literal,
 }
 
 /// A Table Definition
@@ -597,6 +599,10 @@ pub enum BuiltinFunctionKind {
     Codesize,
     /// Table start function
     Tablestart,
+    /// Function signature function
+    FunctionSignature,
+    /// Event hash function
+    EventHash,
 }
 
 impl From<&str> for BuiltinFunctionKind {
@@ -605,6 +611,8 @@ impl From<&str> for BuiltinFunctionKind {
             "__tablesize" => BuiltinFunctionKind::Tablesize,
             "__codesize" => BuiltinFunctionKind::Codesize,
             "__tablestart" => BuiltinFunctionKind::Tablestart,
+            "__FUNC_SIG" => BuiltinFunctionKind::FunctionSignature,
+            "__EVENT_HASH" => BuiltinFunctionKind::EventHash,
             _ => panic!("Invalid Builtin Function Kind"), // TODO: Better error handling
         }
     }
diff --git a/huff_utils/src/error.rs b/huff_utils/src/error.rs
index 253de72..ded95ef 100644
--- a/huff_utils/src/error.rs
+++ b/huff_utils/src/error.rs
@@ -135,6 +135,10 @@ pub enum CodegenErrorKind {
     InvalidMacroStatement,
     /// The Macro Definition is Missing
     MissingMacroDefinition(String),
+    /// The Function Interface is Missing
+    MissingFunctionInterface(String),
+    /// The Event Interface is Missing
+    MissingEventInterface(String),
     /// Missing Constant Definition
     MissingConstantDefinition(String),
     /// Abi Generation Failure
@@ -151,6 +155,8 @@ pub enum CodegenErrorKind {
     InvalidMacroInvocation(String),
     /// Conversion Error for usize
     UsizeConversion(String),
+    /// Invalid Arguments
+    InvalidArguments(String),
 }
 
 impl Spanned for CodegenError {
@@ -172,6 +178,12 @@ impl<W: Write> Report<W> for CodegenError {
             CodegenErrorKind::MissingMacroDefinition(str) => {
                 write!(f.out, "Missing Macro \"{}\" Definition!", str)
             }
+            CodegenErrorKind::MissingFunctionInterface(str) => {
+                write!(f.out, "Missing Function Interface for \"{}\"!", str)
+            }
+            CodegenErrorKind::MissingEventInterface(str) => {
+                write!(f.out, "Missing Event Interface for \"{}\"!", str)
+            }
             CodegenErrorKind::MissingConstantDefinition(cd) => {
                 write!(f.out, "Missing Constant Definition for \"{}\"!", cd)
             }
@@ -185,6 +197,9 @@ impl<W: Write> Report<W> for CodegenError {
             CodegenErrorKind::UsizeConversion(input) => {
                 write!(f.out, "Usize Conversion Failed for \"{}\"", input)
             }
+            CodegenErrorKind::InvalidArguments(msg) => {
+                write!(f.out, "Invalid arguments: \"{}\"", msg)
+            }
         }
     }
 }
@@ -429,6 +444,22 @@ impl<'a> fmt::Display for CompilerError<'a> {
                         ce.span.error(None)
                     )
                 }
+                CodegenErrorKind::MissingFunctionInterface(func) => {
+                    write!(
+                        f,
+                        "\nError: Missing Function Interface: \"{}\"\n{}\n",
+                        func,
+                        ce.span.error(None)
+                    )
+                }
+                CodegenErrorKind::MissingEventInterface(event) => {
+                    write!(
+                        f,
+                        "\nError: Missing Event Interface: \"{}\"\n{}\n",
+                        event,
+                        ce.span.error(None)
+                    )
+                }
                 CodegenErrorKind::MissingConstantDefinition(_) => {
                     write!(f, "\nError: Missing Constant Definition\n{}\n", ce.span.error(None))
                 }
@@ -455,6 +486,9 @@ impl<'a> fmt::Display for CompilerError<'a> {
                 CodegenErrorKind::UsizeConversion(_) => {
                     write!(f, "\nError: Usize Conversion\n{}\n", ce.span.error(None))
                 }
+                CodegenErrorKind::InvalidArguments(_) => {
+                    write!(f, "\nError: Invalid Arguments\n{}\n", ce.span.error(None))
+                }
             },
             CompilerError::FailedCompiles(v) => {
                 v.iter().for_each(|ce| {


Test patch:
diff --git a/huff_core/tests/builtins.rs b/huff_core/tests/builtins.rs
index 49c3ab0..8851dd7 100644
--- a/huff_core/tests/builtins.rs
+++ b/huff_core/tests/builtins.rs
@@ -41,7 +41,6 @@ fn test_codesize_builtin() {
 
     // Have the Codegen create the constructor bytecode
     let cbytes = Codegen::generate_constructor_bytecode(&contract).unwrap();
-    println!("Constructor Bytecode Result: {:?}", cbytes);
     assert_eq!(cbytes, String::from("6004"));
 }
 
@@ -173,7 +172,6 @@ fn test_tablestart_builtin() {
 
     // Have the Codegen create the constructor bytecode
     let cbytes = Codegen::generate_constructor_bytecode(&contract).unwrap();
-    println!("Constructor Bytecode Result: {:?}", cbytes);
     assert_eq!(cbytes, String::from("61001e6100265b60006000f35b60006000f35b60006000f35b60006000f30006000c001200180000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018"));
 }
 
@@ -366,3 +364,106 @@ fn test_label_clashing() {
     let mbytes = Codegen::generate_main_bytecode(&contract).unwrap();
     assert_eq!(mbytes, String::from("600861004960003961012861005160003960003560e01c8063a9059cbb14610022575b60208703516202ffe016806020015b60206020015b60206020015b60206020015b602060200100310037003d004300000000000000000000000000000000000000000000000000000000000000310000000000000000000000000000000000000000000000000000000000000037000000000000000000000000000000000000000000000000000000000000003d0000000000000000000000000000000000000000000000000000000000000043"));
 }
+
+#[test]
+fn test_func_sig_builtin() {
+    let source: &str = r#"
+        #define function transfer(address,uint256) nonpayable returns ()
+
+        #define macro TRANSFER() = takes (0) returns (0) {
+            // ...
+        }
+
+        #define macro MAIN() = takes(0) returns (0) {
+            // Identify which function is being called.
+            0x00 calldataload 0xE0 shr
+            dup1 __FUNC_SIG("transfer(address,uint256)") eq transfer jumpi
+            dup1 __FUNC_SIG('transfer(address,uint256)') eq transfer jumpi
+            dup1 __FUNC_SIG(transfer) eq transfer jumpi
+
+            transfer:
+                TRANSFER()
+        }
+    "#;
+
+    // Parse tokens
+    let flattened_source = FullFileSource { source, file: None, spans: vec![] };
+    let lexer = Lexer::new(flattened_source);
+    let tokens = lexer.into_iter().map(|x| x.unwrap()).collect::<Vec<Token>>();
+    let mut parser = Parser::new(tokens, None);
+
+    // Parse the AST
+    let mut contract = parser.parse().unwrap();
+
+    // Derive storage pointers
+    contract.derive_storage_pointers();
+
+    // Instantiate Codegen
+    let cg = Codegen::new();
+
+    // The codegen instance should have no artifact
+    assert!(cg.artifact.is_none());
+
+    // Have the Codegen create the constructor bytecode
+    let cbytes = Codegen::generate_main_bytecode(&contract).unwrap();
+    // `transfer(address,uint256) signature = 0xa9059cbb
+    assert_eq!(&cbytes[16..24], "a9059cbb");
+    assert_eq!(&cbytes[38..46], "a9059cbb");
+    assert_eq!(&cbytes[60..68], "a9059cbb");
+    assert_eq!(
+        cbytes,
+        String::from(
+            "60003560e01c8063a9059cbb14610027578063a9059cbb14610027578063a9059cbb14610027575b"
+        )
+    );
+}
+
+#[test]
+fn test_event_hash_builtin() {
+    let source: &str = r#"
+        #define event transfer(address,address,uint256)
+
+        #define macro MAIN() = takes(0) returns (0) {
+            __EVENT_HASH("transfer(address,address,uint256)")
+            __EVENT_HASH('transfer(address,address,uint256)')
+            __EVENT_HASH(transfer)
+            0x00 sstore
+        }
+    "#;
+
+    // Parse tokens
+    let flattened_source = FullFileSource { source, file: None, spans: vec![] };
+    let lexer = Lexer::new(flattened_source);
+    let tokens = lexer.into_iter().map(|x| x.unwrap()).collect::<Vec<Token>>();
+    let mut parser = Parser::new(tokens, None);
+
+    // Parse the AST
+    let mut contract = parser.parse().unwrap();
+
+    // Derive storage pointers
+    contract.derive_storage_pointers();
+
+    // Instantiate Codegen
+    let cg = Codegen::new();
+
+    // The codegen instance should have no artifact
+    assert!(cg.artifact.is_none());
+
+    // Have the Codegen create the constructor bytecode
+    let cbytes = Codegen::generate_main_bytecode(&contract).unwrap();
+    // `transfer(address,address,uint256) signature =
+    // 0xbeabacc8ffedac16e9a60acdb2ca743d80c2ebb44977a93fa8e483c74d2b35a8
+    assert_eq!(&cbytes[2..66], "beabacc8ffedac16e9a60acdb2ca743d80c2ebb44977a93fa8e483c74d2b35a8");
+    assert_eq!(
+        &cbytes[68..132],
+        "beabacc8ffedac16e9a60acdb2ca743d80c2ebb44977a93fa8e483c74d2b35a8"
+    );
+    assert_eq!(
+        &cbytes[134..198],
+        "beabacc8ffedac16e9a60acdb2ca743d80c2ebb44977a93fa8e483c74d2b35a8"
+    );
+    assert_eq!(
+        cbytes,
+        String::from("7fbeabacc8ffedac16e9a60acdb2ca743d80c2ebb44977a93fa8e483c74d2b35a87fbeabacc8ffedac16e9a60acdb2ca743d80c2ebb44977a93fa8e483c74d2b35a87fbeabacc8ffedac16e9a60acdb2ca743d80c2ebb44977a93fa8e483c74d2b35a8600055")
+    );
+}
diff --git a/huff_core/tests/parser_errors.rs b/huff_core/tests/parser_errors.rs
index 6dd8b47..dd8a150 100644
--- a/huff_core/tests/parser_errors.rs
+++ b/huff_core/tests/parser_errors.rs
@@ -250,7 +250,10 @@ fn test_invalid_token_in_label_definition() {
 fn test_invalid_single_arg() {
     for _ in [0..10_000] {
         let random_char = rand::random::<u8>() as char;
-        if random_char.is_numeric() || !random_char.is_alphabetic() {
+        if random_char.is_numeric() ||
+            !random_char.is_alphabetic() ||
+            random_char.to_string().as_bytes().len() > 1
+        {
             continue
         }
         let source =
diff --git a/huff_lexer/tests/builtins.rs b/huff_lexer/tests/builtins.rs
index 1765147..cce87e8 100644
--- a/huff_lexer/tests/builtins.rs
+++ b/huff_lexer/tests/builtins.rs
@@ -4,7 +4,7 @@ use std::ops::Deref;
 
 #[test]
 fn parses_builtin_function_in_macro_body() {
-    let builtin_funcs = ["__codesize", "__tablesize", "__tablestart"];
+    let builtin_funcs = ["__codesize", "__tablesize", "__tablestart", "__FUNC_SIG", "__EVENT_HASH"];
 
     for builtin in builtin_funcs {
         let source = &format!(
@@ -69,7 +69,7 @@ fn parses_builtin_function_in_macro_body() {
 #[test]
 #[should_panic]
 fn fails_to_parse_builtin_outside_macro_body() {
-    let builtin_funcs = ["__codesize", "__tablesize", "__tablestart"];
+    let builtin_funcs = ["__codesize", "__tablesize", "__tablestart", "__FUNC_SIG", "__EVENT_HASH"];
 
     for builtin in builtin_funcs {
         let source = &format!("{}(MAIN)", builtin);
diff --git a/huff_parser/tests/event.rs b/huff_parser/tests/event.rs
index dda551b..b24750f 100644
--- a/huff_parser/tests/event.rs
+++ b/huff_parser/tests/event.rs
@@ -59,6 +59,10 @@ fn test_parse_event() {
                     // ")"
                     Span { start: 55, end: 56, file: None },
                 ]),
+                hash: [
+                    96, 60, 104, 14, 131, 197, 151, 167, 15, 107, 26, 61, 1, 186, 238, 67, 62, 152,
+                    177, 8, 184, 82, 242, 224, 18, 70, 210, 27, 27, 119, 23, 114,
+                ],
             },
         ),
         (
@@ -107,6 +111,10 @@ fn test_parse_event() {
                     // ")"
                     Span { start: 39, end: 40, file: None },
                 ]),
+                hash: [
+                    96, 60, 104, 14, 131, 197, 151, 167, 15, 107, 26, 61, 1, 186, 238, 67, 62, 152,
+                    177, 8, 184, 82, 242, 224, 18, 70, 210, 27, 27, 119, 23, 114,
+                ],
             },
         ),
         (
@@ -155,6 +163,10 @@ fn test_parse_event() {
                     // ")"
                     Span { start: 45, end: 46, file: None },
                 ]),
+                hash: [
+                    96, 60, 104, 14, 131, 197, 151, 167, 15, 107, 26, 61, 1, 186, 238, 67, 62, 152,
+                    177, 8, 184, 82, 242, 224, 18, 70, 210, 27, 27, 119, 23, 114,
+                ],
             },
         ),
     ];