1use std::collections::HashMap;
9use std::sync::OnceLock;
10
11pub const STOP: u8 = 0x00;
12pub const KECCAK256: u8 = 0x20;
13pub const CALLDATACOPY: u8 = 0x37;
14pub const CODECOPY: u8 = 0x39;
15pub const EXTCODECOPY: u8 = 0x3c;
16pub const RETURNDATACOPY: u8 = 0x3e;
17pub const MLOAD: u8 = 0x51;
18pub const MSTORE: u8 = 0x52;
19pub const MSTORE8: u8 = 0x53;
20pub const SLOAD: u8 = 0x54;
21pub const SSTORE: u8 = 0x55;
22pub const JUMP: u8 = 0x56;
23pub const JUMPI: u8 = 0x57;
24pub const JUMPDEST: u8 = 0x5b;
25pub const TLOAD: u8 = 0x5c;
26pub const TSTORE: u8 = 0x5d;
27pub const MCOPY: u8 = 0x5e;
28pub const CREATE: u8 = 0xf0;
29pub const CALL: u8 = 0xf1;
30pub const CALLCODE: u8 = 0xf2;
31pub const RETURN: u8 = 0xf3;
32pub const DELEGATECALL: u8 = 0xf4;
33pub const CREATE2: u8 = 0xf5;
34pub const STATICCALL: u8 = 0xfa;
35pub const REVERT: u8 = 0xfd;
36pub const INVALID: u8 = 0xfe;
37pub const SELFDESTRUCT: u8 = 0xff;
38
39const NAMES: [&str; 256] = [
41 "STOP",
42 "ADD",
43 "MUL",
44 "SUB",
45 "DIV",
46 "SDIV",
47 "MOD",
48 "SMOD",
49 "ADDMOD",
50 "MULMOD",
51 "EXP",
52 "SIGNEXTEND",
53 "UNKNOWN(0x0c)",
54 "UNKNOWN(0x0d)",
55 "UNKNOWN(0x0e)",
56 "UNKNOWN(0x0f)",
57 "LT",
58 "GT",
59 "SLT",
60 "SGT",
61 "EQ",
62 "ISZERO",
63 "AND",
64 "OR",
65 "XOR",
66 "NOT",
67 "BYTE",
68 "SHL",
69 "SHR",
70 "SAR",
71 "CLZ",
72 "UNKNOWN(0x1f)",
73 "KECCAK256",
74 "UNKNOWN(0x21)",
75 "UNKNOWN(0x22)",
76 "UNKNOWN(0x23)",
77 "UNKNOWN(0x24)",
78 "UNKNOWN(0x25)",
79 "UNKNOWN(0x26)",
80 "UNKNOWN(0x27)",
81 "UNKNOWN(0x28)",
82 "UNKNOWN(0x29)",
83 "UNKNOWN(0x2a)",
84 "UNKNOWN(0x2b)",
85 "UNKNOWN(0x2c)",
86 "UNKNOWN(0x2d)",
87 "UNKNOWN(0x2e)",
88 "UNKNOWN(0x2f)",
89 "ADDRESS",
90 "BALANCE",
91 "ORIGIN",
92 "CALLER",
93 "CALLVALUE",
94 "CALLDATALOAD",
95 "CALLDATASIZE",
96 "CALLDATACOPY",
97 "CODESIZE",
98 "CODECOPY",
99 "GASPRICE",
100 "EXTCODESIZE",
101 "EXTCODECOPY",
102 "RETURNDATASIZE",
103 "RETURNDATACOPY",
104 "EXTCODEHASH",
105 "BLOCKHASH",
106 "COINBASE",
107 "TIMESTAMP",
108 "NUMBER",
109 "DIFFICULTY",
110 "GASLIMIT",
111 "CHAINID",
112 "SELFBALANCE",
113 "BASEFEE",
114 "BLOBHASH",
115 "BLOBBASEFEE",
116 "SLOTNUM",
117 "UNKNOWN(0x4c)",
118 "UNKNOWN(0x4d)",
119 "UNKNOWN(0x4e)",
120 "UNKNOWN(0x4f)",
121 "POP",
122 "MLOAD",
123 "MSTORE",
124 "MSTORE8",
125 "SLOAD",
126 "SSTORE",
127 "JUMP",
128 "JUMPI",
129 "PC",
130 "MSIZE",
131 "GAS",
132 "JUMPDEST",
133 "TLOAD",
134 "TSTORE",
135 "MCOPY",
136 "PUSH0",
137 "PUSH1",
138 "PUSH2",
139 "PUSH3",
140 "PUSH4",
141 "PUSH5",
142 "PUSH6",
143 "PUSH7",
144 "PUSH8",
145 "PUSH9",
146 "PUSH10",
147 "PUSH11",
148 "PUSH12",
149 "PUSH13",
150 "PUSH14",
151 "PUSH15",
152 "PUSH16",
153 "PUSH17",
154 "PUSH18",
155 "PUSH19",
156 "PUSH20",
157 "PUSH21",
158 "PUSH22",
159 "PUSH23",
160 "PUSH24",
161 "PUSH25",
162 "PUSH26",
163 "PUSH27",
164 "PUSH28",
165 "PUSH29",
166 "PUSH30",
167 "PUSH31",
168 "PUSH32",
169 "DUP1",
170 "DUP2",
171 "DUP3",
172 "DUP4",
173 "DUP5",
174 "DUP6",
175 "DUP7",
176 "DUP8",
177 "DUP9",
178 "DUP10",
179 "DUP11",
180 "DUP12",
181 "DUP13",
182 "DUP14",
183 "DUP15",
184 "DUP16",
185 "SWAP1",
186 "SWAP2",
187 "SWAP3",
188 "SWAP4",
189 "SWAP5",
190 "SWAP6",
191 "SWAP7",
192 "SWAP8",
193 "SWAP9",
194 "SWAP10",
195 "SWAP11",
196 "SWAP12",
197 "SWAP13",
198 "SWAP14",
199 "SWAP15",
200 "SWAP16",
201 "LOG0",
202 "LOG1",
203 "LOG2",
204 "LOG3",
205 "LOG4",
206 "UNKNOWN(0xa5)",
207 "UNKNOWN(0xa6)",
208 "UNKNOWN(0xa7)",
209 "UNKNOWN(0xa8)",
210 "UNKNOWN(0xa9)",
211 "UNKNOWN(0xaa)",
212 "UNKNOWN(0xab)",
213 "UNKNOWN(0xac)",
214 "UNKNOWN(0xad)",
215 "UNKNOWN(0xae)",
216 "UNKNOWN(0xaf)",
217 "UNKNOWN(0xb0)",
218 "UNKNOWN(0xb1)",
219 "UNKNOWN(0xb2)",
220 "UNKNOWN(0xb3)",
221 "UNKNOWN(0xb4)",
222 "UNKNOWN(0xb5)",
223 "UNKNOWN(0xb6)",
224 "UNKNOWN(0xb7)",
225 "UNKNOWN(0xb8)",
226 "UNKNOWN(0xb9)",
227 "UNKNOWN(0xba)",
228 "UNKNOWN(0xbb)",
229 "UNKNOWN(0xbc)",
230 "UNKNOWN(0xbd)",
231 "UNKNOWN(0xbe)",
232 "UNKNOWN(0xbf)",
233 "UNKNOWN(0xc0)",
234 "UNKNOWN(0xc1)",
235 "UNKNOWN(0xc2)",
236 "UNKNOWN(0xc3)",
237 "UNKNOWN(0xc4)",
238 "UNKNOWN(0xc5)",
239 "UNKNOWN(0xc6)",
240 "UNKNOWN(0xc7)",
241 "UNKNOWN(0xc8)",
242 "UNKNOWN(0xc9)",
243 "UNKNOWN(0xca)",
244 "UNKNOWN(0xcb)",
245 "UNKNOWN(0xcc)",
246 "UNKNOWN(0xcd)",
247 "UNKNOWN(0xce)",
248 "UNKNOWN(0xcf)",
249 "UNKNOWN(0xd0)",
250 "UNKNOWN(0xd1)",
251 "UNKNOWN(0xd2)",
252 "UNKNOWN(0xd3)",
253 "UNKNOWN(0xd4)",
254 "UNKNOWN(0xd5)",
255 "UNKNOWN(0xd6)",
256 "UNKNOWN(0xd7)",
257 "UNKNOWN(0xd8)",
258 "UNKNOWN(0xd9)",
259 "UNKNOWN(0xda)",
260 "UNKNOWN(0xdb)",
261 "UNKNOWN(0xdc)",
262 "UNKNOWN(0xdd)",
263 "UNKNOWN(0xde)",
264 "UNKNOWN(0xdf)",
265 "UNKNOWN(0xe0)",
266 "UNKNOWN(0xe1)",
267 "UNKNOWN(0xe2)",
268 "UNKNOWN(0xe3)",
269 "UNKNOWN(0xe4)",
270 "UNKNOWN(0xe5)",
271 "DUPN",
272 "SWAPN",
273 "EXCHANGE",
274 "UNKNOWN(0xe9)",
275 "UNKNOWN(0xea)",
276 "UNKNOWN(0xeb)",
277 "UNKNOWN(0xec)",
278 "UNKNOWN(0xed)",
279 "UNKNOWN(0xee)",
280 "UNKNOWN(0xef)",
281 "CREATE",
282 "CALL",
283 "CALLCODE",
284 "RETURN",
285 "DELEGATECALL",
286 "CREATE2",
287 "UNKNOWN(0xf6)",
288 "UNKNOWN(0xf7)",
289 "UNKNOWN(0xf8)",
290 "UNKNOWN(0xf9)",
291 "STATICCALL",
292 "UNKNOWN(0xfb)",
293 "UNKNOWN(0xfc)",
294 "REVERT",
295 "INVALID",
296 "SELFDESTRUCT",
297];
298
299#[must_use]
301pub fn mnemonic(op: u8) -> &'static str {
302 NAMES[usize::from(op)]
303}
304
305#[must_use]
307pub fn from_mnemonic(name: &str) -> Option<u8> {
308 static BYTES: OnceLock<HashMap<&'static str, u8>> = OnceLock::new();
309 BYTES
310 .get_or_init(|| (0..=u8::MAX).map(|op| (mnemonic(op), op)).collect())
311 .get(name)
312 .copied()
313}
314
315#[must_use]
319pub fn from_spelling(name: &str) -> Option<u8> {
320 if let Some(op) = from_mnemonic(name) {
321 return Some(op);
322 }
323 match name {
324 "SHA3" => Some(KECCAK256),
325 "PREVRANDAO" | "RANDOM" => Some(0x44),
326 "SUICIDE" => Some(SELFDESTRUCT),
327 _ => {
328 let start = name.find("0x").or_else(|| name.find("0X"))? + 2;
329 let digits: String = name
330 .get(start..)?
331 .chars()
332 .take_while(char::is_ascii_hexdigit)
333 .collect();
334 if digits.is_empty() || digits.len() > 2 {
335 return None;
336 }
337 u8::from_str_radix(&digits, 16).ok()
338 }
339 }
340}
341
342#[must_use]
344pub fn is_call(op: u8) -> bool {
345 matches!(op, CALL | CALLCODE | DELEGATECALL | STATICCALL)
346}
347
348#[cfg(test)]
349mod tests {
350 use super::*;
351
352 #[test]
353 fn every_byte_round_trips_through_its_mnemonic() {
354 for op in 0..=u8::MAX {
355 assert_eq!(from_mnemonic(mnemonic(op)), Some(op), "{}", mnemonic(op));
356 }
357 assert_eq!(mnemonic(SSTORE), "SSTORE");
358 assert_eq!(mnemonic(0x0c), "UNKNOWN(0x0c)");
359 assert_eq!(from_mnemonic("SHA3"), None);
360 }
361
362 #[test]
363 fn other_clients_spellings_name_the_same_byte() {
364 assert_eq!(from_spelling("SHA3"), Some(KECCAK256));
365 assert_eq!(from_spelling("PREVRANDAO"), Some(0x44));
366 assert_eq!(from_spelling("opcode 0xef not defined"), Some(0xef));
367 assert_eq!(from_spelling("UNKNOWN(0x0c)"), Some(0x0c));
368 assert_eq!(from_spelling("WHATEVER"), None);
369 assert_eq!(from_spelling("opcode 0x123"), None);
370 }
371}