huff_neo_utils/
builtin_eval.rs1use crate::ast::span::AstSpan;
7use crate::error::{CodegenError, CodegenErrorKind};
8use crate::prelude::{BuiltinFunctionArg, BuiltinFunctionCall, Contract, PushValue};
9use alloy_primitives::{B256, hex, keccak256};
10
11fn invalid_arguments_error(msg: impl Into<String>, span: &AstSpan) -> CodegenError {
13 CodegenError { kind: CodegenErrorKind::InvalidArguments(msg.into()), span: span.clone_box(), token: None }
14}
15
16fn invalid_hex_error(hex_str: impl Into<String>, span: &AstSpan) -> CodegenError {
18 CodegenError { kind: CodegenErrorKind::InvalidHex(hex_str.into()), span: span.clone_box(), token: None }
19}
20
21fn validate_arg_count(bf: &BuiltinFunctionCall, expected: usize, fn_name: &str) -> Result<(), CodegenError> {
23 if bf.args.len() != expected {
24 return Err(invalid_arguments_error(
25 format!("Incorrect number of arguments passed to {}, should be {}: {}", fn_name, expected, bf.args.len()),
26 &bf.span,
27 ));
28 }
29 Ok(())
30}
31
32pub fn eval_event_hash(contract: &Contract, bf: &BuiltinFunctionCall) -> Result<PushValue, CodegenError> {
34 validate_arg_count(bf, 1, "__EVENT_HASH")?;
35
36 let arg_str = match &bf.args[0] {
37 BuiltinFunctionArg::StringLiteral(s, _) => s.clone(),
38 BuiltinFunctionArg::Identifier(name, _) => name.clone(),
39 _ => {
40 return Err(invalid_arguments_error("Expected string literal or identifier as argument to __EVENT_HASH", &bf.span));
41 }
42 };
43
44 let hash = if let Some(event) = contract.events.iter().find(|e| e.name.eq(&arg_str)) { event.hash } else { keccak256(&arg_str).0 };
45 Ok(PushValue::from(hash))
46}
47
48pub fn eval_function_signature(contract: &Contract, bf: &BuiltinFunctionCall) -> Result<PushValue, CodegenError> {
50 validate_arg_count(bf, 1, "__FUNC_SIG")?;
51
52 let arg_str = match &bf.args[0] {
53 BuiltinFunctionArg::StringLiteral(s, _) => s.clone(),
54 BuiltinFunctionArg::Identifier(name, _) => name.clone(),
55 _ => {
56 return Err(invalid_arguments_error("Expected string literal or identifier as argument to __FUNC_SIG", &bf.span));
57 }
58 };
59
60 let selector = if let Some(func) = contract.functions.iter().find(|f| f.name.eq(&arg_str)) {
61 func.signature
62 } else if let Some(error) = contract.errors.iter().find(|e| e.name.eq(&arg_str)) {
63 error.selector
64 } else {
65 keccak256(&arg_str)[..4].try_into().unwrap()
66 };
67
68 let mut bytes = [0u8; 32];
70 bytes[28..32].copy_from_slice(&selector);
71 Ok(PushValue::from(bytes))
72}
73
74pub fn eval_builtin_bytes(bf: &BuiltinFunctionCall) -> Result<PushValue, CodegenError> {
76 validate_arg_count(bf, 1, "__BYTES")?;
77 let first_arg = match &bf.args[0] {
78 BuiltinFunctionArg::StringLiteral(s, _) => s.clone(),
79 _ => {
80 return Err(invalid_arguments_error("Expected string literal as argument to __BYTES", &bf.span));
81 }
82 };
83
84 if first_arg.is_empty() {
85 return Err(invalid_arguments_error("Empty string passed to __BYTES", &bf.span));
86 }
87
88 let bytes = first_arg.as_bytes();
89 if bytes.len() > 32 {
90 return Err(invalid_arguments_error("Encoded bytes length exceeds 32 bytes", &bf.span));
91 }
92
93 let mut bytes_array = [0u8; 32];
94 bytes_array[32 - bytes.len()..].copy_from_slice(bytes);
95 Ok(PushValue::from(bytes_array))
96}
97
98#[derive(Debug, Clone, Copy, PartialEq)]
100pub enum PadDirection {
101 Left,
103 Right,
105}
106
107impl std::fmt::Display for PadDirection {
108 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109 match self {
110 PadDirection::Left => write!(f, "__LEFTPAD"),
111 PadDirection::Right => write!(f, "__RIGHTPAD"),
112 }
113 }
114}
115
116pub fn eval_builtin_pad_simple(bf: &BuiltinFunctionCall, direction: PadDirection) -> Result<PushValue, CodegenError> {
121 validate_arg_count(bf, 1, &direction.to_string())?;
122
123 let first_arg = match &bf.args[0] {
124 BuiltinFunctionArg::HexLiteral(hex, _) => hex.clone(),
125 BuiltinFunctionArg::BuiltinFunctionCall(inner_call) => {
126 let push_value = match inner_call.kind {
128 crate::prelude::BuiltinFunctionKind::FunctionSignature => eval_function_signature(&Contract::default(), inner_call)?,
129 crate::prelude::BuiltinFunctionKind::Bytes => eval_builtin_bytes(inner_call)?,
130 crate::prelude::BuiltinFunctionKind::EventHash => eval_event_hash(&Contract::default(), inner_call)?,
131 _ => {
132 return Err(invalid_arguments_error(format!("Invalid function call argument type passed to {direction}"), &bf.span));
133 }
134 };
135 push_value.to_hex_trimmed()
136 }
137 _ => {
138 return Err(invalid_arguments_error(
139 format!("Expected hex literal or builtin function call as argument to {direction}"),
140 &bf.span,
141 ));
142 }
143 };
144
145 let hex = if first_arg.len() % 2 == 1 {
149 if direction == PadDirection::Right { format!("{first_arg}0") } else { format!("0{first_arg}") }
150 } else {
151 first_arg
152 };
153
154 let hex_bytes = hex::decode(&hex).map_err(|_| invalid_hex_error(&hex, &bf.span))?;
156
157 if hex_bytes.len() > 32 {
158 return Err(invalid_arguments_error("Hex string exceeds 32 bytes", &bf.span));
159 }
160
161 let mut bytes_array = [0u8; 32];
162 if direction == PadDirection::Left {
163 let start_idx = 32 - hex_bytes.len();
165 bytes_array[start_idx..].copy_from_slice(&hex_bytes);
166 } else {
167 bytes_array[..hex_bytes.len()].copy_from_slice(&hex_bytes);
169 }
170
171 Ok(PushValue::new(B256::from(bytes_array)))
172}