neo_solidity/ir/build/
literals.rs

1fn literal_from_expression(expr: &Expression) -> Option<LiteralValue> {
2    match expr {
3        Expression::BoolLiteral(_, value) => Some(LiteralValue::Boolean(*value)),
4        Expression::NumberLiteral(_, integer, exp, unit) => {
5            let mut value = parse_decimal_bigint(integer)?;
6            let exponent = parse_signed_decimal_i32(exp)?;
7
8            if exponent >= 0 {
9                value *= pow10(exponent as u32);
10            } else {
11                let divisor = pow10((-exponent) as u32);
12                if (&value % &divisor).is_zero() {
13                    value /= divisor;
14                } else {
15                    return None;
16                }
17            }
18
19            if let Some(unit) = unit.as_ref() {
20                value *= unit_multiplier(unit)?;
21            }
22
23            Some(LiteralValue::Integer(value))
24        }
25        Expression::HexNumberLiteral(_, value, unit) => {
26            let mut number = parse_hex_bigint(value)?;
27            if let Some(unit) = unit.as_ref() {
28                number *= unit_multiplier(unit)?;
29            }
30            Some(LiteralValue::Integer(number))
31        }
32        Expression::RationalNumberLiteral(_, integer, fraction, exp, unit) => {
33            let int_part = parse_decimal_bigint(integer)?;
34            let fraction_digits = sanitize_numeric_token(fraction);
35            let frac_len = fraction_digits.len() as u32;
36            let frac_part = if fraction_digits.trim().is_empty() {
37                BigInt::zero()
38            } else {
39                BigInt::parse_bytes(fraction_digits.as_bytes(), 10)?
40            };
41
42            let mut numerator = int_part * pow10(frac_len) + frac_part;
43            let mut denominator = pow10(frac_len);
44
45            let exponent = parse_signed_decimal_i32(exp)?;
46            if exponent >= 0 {
47                numerator *= pow10(exponent as u32);
48            } else {
49                denominator *= pow10((-exponent) as u32);
50            }
51
52            if let Some(unit) = unit.as_ref() {
53                numerator *= unit_multiplier(unit)?;
54            }
55
56            if denominator.is_zero() {
57                return None;
58            }
59
60            if (&numerator % &denominator).is_zero() {
61                Some(LiteralValue::Integer(numerator / denominator))
62            } else {
63                eprintln!(
64                    "warning: non-integer rational literal {}.{} cannot be represented \
65                     as an integer; fractional values are not supported on NeoVM",
66                    integer, fraction
67                );
68                None
69            }
70        }
71        Expression::StringLiteral(parts) => Some(LiteralValue::String(string_literal_bytes(parts))),
72        Expression::HexLiteral(parts) => decode_hex_segments(parts).map(LiteralValue::ByteArray),
73        Expression::AddressLiteral(_, value) => decode_hex_bytes(value).and_then(|mut bytes| {
74            // Neo addresses are UInt160 (20 bytes). Reject malformed literals early.
75            if bytes.len() != 20 {
76                eprintln!(
77                    "warning: address literal has {} bytes, expected 20 (UInt160)",
78                    bytes.len()
79                );
80                return None;
81            }
82            // Neo smart contracts treat UInt160 values (script hashes) in little-endian byte
83            // order on the VM stack. Solidity address literals are written in the canonical
84            // big-endian hex form, so we reverse here to match Neo N3 conventions.
85            bytes.reverse();
86            Some(LiteralValue::Address(bytes))
87        }),
88        Expression::Parenthesis(_, inner) => literal_from_expression(inner),
89        _ => None,
90    }
91}
92
93fn address_bytes_le_from_expression(expr: &Expression) -> Option<Vec<u8>> {
94    match expr {
95        Expression::Parenthesis(_, inner) => address_bytes_le_from_expression(inner),
96        Expression::AddressLiteral(_, value) => decode_hex_bytes(value).map(|mut bytes| {
97            if bytes.len() > 20 {
98                bytes.truncate(20);
99            } else if bytes.len() < 20 {
100                let mut padded = vec![0u8; 20 - bytes.len()];
101                padded.extend_from_slice(&bytes);
102                bytes = padded;
103            }
104            bytes.reverse();
105            bytes
106        }),
107        Expression::HexNumberLiteral(_, value, unit) if unit.is_none() => {
108            let raw = value.trim().trim_start_matches("0x");
109            let mut hex: String = raw
110                .chars()
111                .filter(|c| !c.is_whitespace() && *c != '_')
112                .collect();
113            if hex.is_empty() {
114                return None;
115            }
116
117            if hex.len() > 40 {
118                return None;
119            }
120
121            if hex.len() % 2 == 1 {
122                hex.insert(0, '0');
123            }
124
125            if hex.len() < 40 {
126                hex = format!("{:0>40}", hex);
127            }
128
129            let mut bytes = hex_decode(&hex).ok()?;
130            if bytes.len() != 20 {
131                return None;
132            }
133            bytes.reverse();
134            Some(bytes)
135        }
136        _ => None,
137    }
138}
139
140fn decode_hex_segments(parts: &[PtHexLiteral]) -> Option<Vec<u8>> {
141    let mut bytes = Vec::new();
142    for part in parts {
143        let segment = part.hex.trim();
144        let inner = segment
145            .strip_prefix("hex")
146            .and_then(|s| s.trim().strip_prefix('\"'))
147            .and_then(|s| s.strip_suffix('\"'))
148            .unwrap_or(segment);
149        let cleaned: String = inner.chars().filter(|c| !c.is_whitespace()).collect();
150        bytes.extend(hex_decode(&cleaned).ok()?);
151    }
152    Some(bytes)
153}
154
155fn decode_hex_bytes(value: &str) -> Option<Vec<u8>> {
156    let cleaned = value.trim();
157    if let Some(inner) = cleaned.strip_prefix("0x") {
158        hex_decode(inner).ok()
159    } else {
160        hex_decode(cleaned).ok()
161    }
162}
163
164fn parse_decimal_bigint(value: &str) -> Option<BigInt> {
165    let sanitized: String = value.chars().filter(|c| *c != '_').collect();
166    BigInt::parse_bytes(sanitized.as_bytes(), 10)
167}
168
169fn sanitize_numeric_token(value: &str) -> String {
170    value.chars().filter(|c| *c != '_').collect()
171}
172
173fn parse_signed_decimal_i32(value: &str) -> Option<i32> {
174    let sanitized = sanitize_numeric_token(value);
175    if sanitized.trim().is_empty() {
176        Some(0)
177    } else {
178        sanitized.parse::<i32>().ok()
179    }
180}
181
182fn unit_multiplier(unit: &Identifier) -> Option<BigInt> {
183    match unit.name.as_str() {
184        // Solidity ether units
185        "wei" => Some(BigInt::one()),
186        "gwei" => Some(pow10(9)),
187        "szabo" => Some(pow10(12)),
188        "finney" => Some(pow10(15)),
189        "ether" => Some(pow10(18)),
190
191        // Solidity time units (Neo Runtime.GetTime is milliseconds; we normalize block.timestamp
192        // elsewhere to seconds to preserve Solidity semantics.)
193        "second" | "seconds" => Some(BigInt::one()),
194        "minute" | "minutes" => Some(BigInt::from(60u64)),
195        "hour" | "hours" => Some(BigInt::from(60u64 * 60)),
196        "day" | "days" => Some(BigInt::from(60u64 * 60 * 24)),
197        "week" | "weeks" => Some(BigInt::from(60u64 * 60 * 24 * 7)),
198        "year" | "years" => Some(BigInt::from(60u64 * 60 * 24 * 365)),
199
200        _ => None,
201    }
202}
203
204fn is_ether_unit(name: &str) -> bool {
205    matches!(name, "wei" | "gwei" | "szabo" | "finney" | "ether")
206}
207
208fn has_ether_unit(expr: &Expression) -> bool {
209    match expr {
210        Expression::NumberLiteral(_, _, _, Some(unit)) => is_ether_unit(&unit.name),
211        Expression::HexNumberLiteral(_, _, Some(unit)) => is_ether_unit(&unit.name),
212        Expression::RationalNumberLiteral(_, _, _, _, Some(unit)) => is_ether_unit(&unit.name),
213        Expression::Parenthesis(_, inner) => has_ether_unit(inner),
214        _ => false,
215    }
216}
217
218fn pow10(exp: u32) -> BigInt {
219    let ten = BigInt::from(10u8);
220    ten.pow(exp)
221}
222
223fn parse_hex_bigint(value: &str) -> Option<BigInt> {
224    let sanitized = value.trim_start_matches("0x");
225    BigInt::parse_bytes(sanitized.as_bytes(), 16)
226}