Skip to main content

huff_neo_utils/
opcodes.rs

1use alloy_primitives::{U256, hex};
2use phf::phf_map;
3use std::fmt;
4use strum_macros::{AsRefStr, EnumString};
5
6use crate::evm_version::SupportedEVMVersions;
7
8/// All the EVM opcodes as a static array
9/// They are arranged in a particular order such that all the opcodes that have common
10/// prefixes are ordered alphabetically by decreasing length to avoid mismatch when lexing.
11/// Example : [origin, or] or [push32, ..., push3]
12pub const OPCODES: [&str; 156] = [
13    "addmod",
14    "address",
15    "add",
16    "and",
17    "balance",
18    "basefee",
19    "blobbasefee",
20    "blobhash",
21    "blockhash",
22    "byte",
23    "callcode",
24    "calldatacopy",
25    "calldataload",
26    "calldatasize",
27    "caller",
28    "callvalue",
29    "call",
30    "chainid",
31    "clz",
32    "codecopy",
33    "codesize",
34    "coinbase",
35    "create2",
36    "create",
37    "delegatecall",
38    "difficulty",
39    "div",
40    "dup10",
41    "dup11",
42    "dup12",
43    "dup13",
44    "dup14",
45    "dup15",
46    "dup16",
47    "dup1",
48    "dup2",
49    "dup3",
50    "dup4",
51    "dup5",
52    "dup6",
53    "dup7",
54    "dup8",
55    "dup9",
56    "dupn",
57    "eq",
58    "exchange",
59    "exp",
60    "extcodecopy",
61    "extcodehash",
62    "extcodesize",
63    "gaslimit",
64    "gasprice",
65    "gas",
66    "gt",
67    "invalid",
68    "iszero",
69    "jumpdest",
70    "jumpi",
71    "jump",
72    "keccak256",
73    "log0",
74    "log1",
75    "log2",
76    "log3",
77    "log4",
78    "lt",
79    "mcopy",
80    "mload",
81    "mod",
82    "msize",
83    "mstore8",
84    "mstore",
85    "mulmod",
86    "mul",
87    "not",
88    "number",
89    "origin",
90    "or",
91    "pc",
92    "pop",
93    "prevrandao",
94    "push0",
95    "push10",
96    "push11",
97    "push12",
98    "push13",
99    "push14",
100    "push15",
101    "push16",
102    "push17",
103    "push18",
104    "push19",
105    "push1",
106    "push20",
107    "push21",
108    "push22",
109    "push23",
110    "push24",
111    "push25",
112    "push26",
113    "push27",
114    "push28",
115    "push29",
116    "push2",
117    "push30",
118    "push31",
119    "push32",
120    "push3",
121    "push4",
122    "push5",
123    "push6",
124    "push7",
125    "push8",
126    "push9",
127    "returndatacopy",
128    "returndatasize",
129    "return",
130    "revert",
131    "sar",
132    "sdiv",
133    "selfbalance",
134    "selfdestruct",
135    "sgt",
136    "sha3",
137    "shl",
138    "shr",
139    "signextend",
140    "sload",
141    "slotnum",
142    "slt",
143    "smod",
144    "sstore",
145    "staticcall",
146    "stop",
147    "sub",
148    "swap10",
149    "swap11",
150    "swap12",
151    "swap13",
152    "swap14",
153    "swap15",
154    "swap16",
155    "swap1",
156    "swap2",
157    "swap3",
158    "swap4",
159    "swap5",
160    "swap6",
161    "swap7",
162    "swap8",
163    "swap9",
164    "swapn",
165    "timestamp",
166    "tload",
167    "tstore",
168    "xor",
169];
170
171/// Hashmap of all the EVM opcodes
172pub static OPCODES_MAP: phf::Map<&'static str, Opcode> = phf_map! {
173    "lt" => Opcode::Lt,
174    "gt" => Opcode::Gt,
175    "slt" => Opcode::Slt,
176    "sgt" => Opcode::Sgt,
177    "eq" => Opcode::Eq,
178    "iszero" => Opcode::Iszero,
179    "and" => Opcode::And,
180    "or" => Opcode::Or,
181    "xor" => Opcode::Xor,
182    "not" => Opcode::Not,
183    "sha3" => Opcode::Keccak256, // backward compatibility for huff-rs
184    "keccak256" => Opcode::Keccak256,
185    "address" => Opcode::Address,
186    "balance" => Opcode::Balance,
187    "origin" => Opcode::Origin,
188    "caller" => Opcode::Caller,
189    "callvalue" => Opcode::Callvalue,
190    "calldataload" => Opcode::Calldataload,
191    "calldatasize" => Opcode::Calldatasize,
192    "calldatacopy" => Opcode::Calldatacopy,
193    "codesize" => Opcode::Codesize,
194    "codecopy" => Opcode::Codecopy,
195    "basefee" => Opcode::Basefee,
196    "blobhash" => Opcode::Blobhash,
197    "blobbasefee" => Opcode::Blobbasefee,
198    "blockhash" => Opcode::Blockhash,
199    "coinbase" => Opcode::Coinbase,
200    "timestamp" => Opcode::Timestamp,
201    "number" => Opcode::Number,
202    "difficulty" => Opcode::Difficulty,
203    "prevrandao" => Opcode::Prevrandao,
204    "gaslimit" => Opcode::Gaslimit,
205    "chainid" => Opcode::Chainid,
206    "clz" => Opcode::Clz,
207    "selfbalance" => Opcode::Selfbalance,
208    "slotnum" => Opcode::Slotnum,
209    "pop" => Opcode::Pop,
210    "mload" => Opcode::Mload,
211    "mstore" => Opcode::Mstore,
212    "mstore8" => Opcode::Mstore8,
213    "sload" => Opcode::Sload,
214    "sstore" => Opcode::Sstore,
215    "jump" => Opcode::Jump,
216    "jumpi" => Opcode::Jumpi,
217    "pc" => Opcode::Pc,
218    "msize" => Opcode::Msize,
219    "mcopy" => Opcode::Mcopy,
220    "push0" => Opcode::Push0,
221    "push1" => Opcode::Push1,
222    "push2" => Opcode::Push2,
223    "push3" => Opcode::Push3,
224    "push4" => Opcode::Push4,
225    "push5" => Opcode::Push5,
226    "push6" => Opcode::Push6,
227    "push7" => Opcode::Push7,
228    "push8" => Opcode::Push8,
229    "push9" => Opcode::Push9,
230    "push10" => Opcode::Push10,
231    "push17" => Opcode::Push17,
232    "push18" => Opcode::Push18,
233    "push19" => Opcode::Push19,
234    "push20" => Opcode::Push20,
235    "push21" => Opcode::Push21,
236    "push22" => Opcode::Push22,
237    "push23" => Opcode::Push23,
238    "push24" => Opcode::Push24,
239    "push25" => Opcode::Push25,
240    "push26" => Opcode::Push26,
241    "dup1" => Opcode::Dup1,
242    "dup2" => Opcode::Dup2,
243    "dup3" => Opcode::Dup3,
244    "dup4" => Opcode::Dup4,
245    "dup5" => Opcode::Dup5,
246    "dup6" => Opcode::Dup6,
247    "dup7" => Opcode::Dup7,
248    "dup8" => Opcode::Dup8,
249    "dup9" => Opcode::Dup9,
250    "dup10" => Opcode::Dup10,
251    "swap1" => Opcode::Swap1,
252    "swap2" => Opcode::Swap2,
253    "swap3" => Opcode::Swap3,
254    "swap4" => Opcode::Swap4,
255    "swap5" => Opcode::Swap5,
256    "swap6" => Opcode::Swap6,
257    "swap7" => Opcode::Swap7,
258    "swap8" => Opcode::Swap8,
259    "swap9" => Opcode::Swap9,
260    "swap10" => Opcode::Swap10,
261    "stop" => Opcode::Stop,
262    "add" => Opcode::Add,
263    "mul" => Opcode::Mul,
264    "sub" => Opcode::Sub,
265    "div" => Opcode::Div,
266    "sdiv" => Opcode::Sdiv,
267    "mod" => Opcode::Mod,
268    "smod" => Opcode::Smod,
269    "addmod" => Opcode::Addmod,
270    "mulmod" => Opcode::Mulmod,
271    "exp" => Opcode::Exp,
272    "signextend" => Opcode::Signextend,
273    "byte" => Opcode::Byte,
274    "shl" => Opcode::Shl,
275    "shr" => Opcode::Shr,
276    "sar" => Opcode::Sar,
277    "gasprice" => Opcode::Gasprice,
278    "extcodesize" => Opcode::Extcodesize,
279    "extcodecopy" => Opcode::Extcodecopy,
280    "returndatasize" => Opcode::Returndatasize,
281    "returndatacopy" => Opcode::Returndatacopy,
282    "extcodehash" => Opcode::Extcodehash,
283    "gas" => Opcode::Gas,
284    "jumpdest" => Opcode::Jumpdest,
285    "push11" => Opcode::Push11,
286    "push12" => Opcode::Push12,
287    "push13" => Opcode::Push13,
288    "push14" => Opcode::Push14,
289    "push15" => Opcode::Push15,
290    "push16" => Opcode::Push16,
291    "push27" => Opcode::Push27,
292    "push28" => Opcode::Push28,
293    "push29" => Opcode::Push29,
294    "push30" => Opcode::Push30,
295    "push31" => Opcode::Push31,
296    "push32" => Opcode::Push32,
297    "dup11" => Opcode::Dup11,
298    "dup12" => Opcode::Dup12,
299    "dup13" => Opcode::Dup13,
300    "dup14" => Opcode::Dup14,
301    "dup15" => Opcode::Dup15,
302    "dup16" => Opcode::Dup16,
303    "dupn" => Opcode::Dupn,
304    "swap11" => Opcode::Swap11,
305    "swap12" => Opcode::Swap12,
306    "swap13" => Opcode::Swap13,
307    "swap14" => Opcode::Swap14,
308    "swap15" => Opcode::Swap15,
309    "swap16" => Opcode::Swap16,
310    "swapn" => Opcode::Swapn,
311    "exchange" => Opcode::Exchange,
312    "log0" => Opcode::Log0,
313    "log1" => Opcode::Log1,
314    "log2" => Opcode::Log2,
315    "log3" => Opcode::Log3,
316    "log4" => Opcode::Log4,
317    "tload" => Opcode::Tload,
318    "tstore" => Opcode::Tstore,
319    "create" => Opcode::Create,
320    "call" => Opcode::Call,
321    "callcode" => Opcode::Callcode,
322    "return" => Opcode::Return,
323    "delegatecall" => Opcode::Delegatecall,
324    "staticcall" => Opcode::Staticcall,
325    "create2" => Opcode::Create2,
326    "revert" => Opcode::Revert,
327    "invalid" => Opcode::Invalid,
328    "selfdestruct" => Opcode::Selfdestruct,
329};
330
331/// EVM Opcodes
332/// References <https://evm.codes> and <https://github.com/ethereum/execution-specs/blob/master/lists/evm/proposed-opcodes.md>
333#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, EnumString, AsRefStr)]
334#[strum(serialize_all = "lowercase")]
335pub enum Opcode {
336    /// Halts execution.
337    Stop,
338    /// Addition operation
339    Add,
340    /// Multiplication Operation
341    Mul,
342    /// Subtraction Operation
343    Sub,
344    /// Integer Division Operation
345    Div,
346    /// Signed Integer Division Operation
347    Sdiv,
348    /// Modulo Remainder Operation
349    Mod,
350    /// Signed Modulo Remainder Operation
351    Smod,
352    /// Modulo Addition Operation
353    Addmod,
354    /// Modulo Multiplication Operation
355    Mulmod,
356    /// Exponential Operation
357    Exp,
358    /// Extend Length of Two's Complement Signed Integer
359    Signextend,
360    /// Less-than Comparison
361    Lt,
362    /// Greater-than Comparison
363    Gt,
364    /// Signed Less-than Comparison
365    Slt,
366    /// Signed Greater-than Comparison
367    Sgt,
368    /// Equality Comparison
369    Eq,
370    /// Not Operation
371    Iszero,
372    /// Bitwise AND Operation
373    And,
374    /// Bitwise OR Operation
375    Or,
376    /// Bitwise XOR Operation
377    Xor,
378    /// Bitwise NOT Operation
379    Not,
380    /// Retrieve Single Byte from Word
381    Byte,
382    /// Left Shift Operation
383    Shl,
384    /// Right Shift Operation
385    Shr,
386    /// Arithmetic Shift Right Operation
387    Sar,
388    /// Compute the Keccak-256 hash of a 32-byte word
389    #[strum(serialize = "keccak256", serialize = "sha3")]
390    Keccak256,
391    /// Address of currently executing account
392    Address,
393    /// Balance of a given account
394    Balance,
395    /// Address of execution origination
396    Origin,
397    /// Address of the caller
398    Caller,
399    /// Value of the call
400    Callvalue,
401    /// Loads Calldata
402    Calldataload,
403    /// Size of the Calldata
404    Calldatasize,
405    /// Copies the Calldata to Memory
406    Calldatacopy,
407    /// Size of the Executing Code
408    Codesize,
409    /// Copies Executing Code to Memory
410    Codecopy,
411    /// Current Price of Gas
412    Gasprice,
413    /// Size of an Account's Code
414    Extcodesize,
415    /// Copies an Account's Code to Memory
416    Extcodecopy,
417    /// Size of Output Data from Previous Call
418    Returndatasize,
419    /// Copies Output Data from Previous Call to Memory
420    Returndatacopy,
421    /// Hash of a Block from the most recent 256 blocks
422    Blockhash,
423    /// The Current Blocks Beneficiary Address
424    Coinbase,
425    /// The Current Blocks Timestamp
426    Timestamp,
427    /// The Current Blocks Number
428    Number,
429    /// The Current Blocks Difficulty
430    Difficulty,
431    /// Pseudorandomness from the Beacon Chain
432    Prevrandao,
433    /// The Current Blocks Gas Limit
434    Gaslimit,
435    /// The Chain ID
436    Chainid,
437    /// Count Leading Zeros - Returns the number of zeros preceding the most significant one bit
438    Clz,
439    /// Balance of the Currently Executing Account
440    Selfbalance,
441    /// Base Fee
442    Basefee,
443    /// Versioned hashes of blobs associated with the transaction.
444    Blobhash,
445    /// Blob base fee of the current block.
446    Blobbasefee,
447    /// Beacon chain slot number of the current block
448    Slotnum,
449    /// Removes an Item from the Stack
450    Pop,
451    /// Loads a word from Memory
452    Mload,
453    /// Stores a word in Memory
454    Mstore,
455    /// Stores a byte in Memory
456    Mstore8,
457    /// Load a word from Storage
458    Sload,
459    /// Store a word in Storage
460    Sstore,
461    /// Alter the Program Counter
462    Jump,
463    /// Conditionally Alter the Program Counter
464    Jumpi,
465    /// Value of the Program Counter Before the Current Instruction
466    Pc,
467    /// Size of Active Memory in Bytes
468    Msize,
469    /// Amount of available gas including the cost of the current instruction
470    Gas,
471    /// Marks a valid destination for jumps
472    Jumpdest,
473    /// Places a zero on top of the stack
474    Push0,
475    /// Places 1 byte item on top of the stack
476    Push1,
477    /// Places 2 byte item on top of the stack
478    Push2,
479    /// Places 3 byte item on top of the stack
480    Push3,
481    /// Places 4 byte item on top of the stack
482    Push4,
483    /// Places 5 byte item on top of the stack
484    Push5,
485    /// Places 6 byte item on top of the stack
486    Push6,
487    /// Places 7 byte item on top of the stack
488    Push7,
489    /// Places 8 byte item on top of the stack
490    Push8,
491    /// Places 9 byte item on top of the stack
492    Push9,
493    /// Places 10 byte item on top of the stack
494    Push10,
495    /// Places 11 byte item on top of the stack
496    Push11,
497    /// Places 12 byte item on top of the stack
498    Push12,
499    /// Places 13 byte item on top of the stack
500    Push13,
501    /// Places 14 byte item on top of the stack
502    Push14,
503    /// Places 15 byte item on top of the stack
504    Push15,
505    /// Places 16 byte item on top of the stack
506    Push16,
507    /// Places 17 byte item on top of the stack
508    Push17,
509    /// Places 18 byte item on top of the stack
510    Push18,
511    /// Places 19 byte item on top of the stack
512    Push19,
513    /// Places 20 byte item on top of the stack
514    Push20,
515    /// Places 21 byte item on top of the stack
516    Push21,
517    /// Places 22 byte item on top of the stack
518    Push22,
519    /// Places 23 byte item on top of the stack
520    Push23,
521    /// Places 24 byte item on top of the stack
522    Push24,
523    /// Places 25 byte item on top of the stack
524    Push25,
525    /// Places 26 byte item on top of the stack
526    Push26,
527    /// Places 27 byte item on top of the stack
528    Push27,
529    /// Places 28 byte item on top of the stack
530    Push28,
531    /// Places 29 byte item on top of the stack
532    Push29,
533    /// Places 30 byte item on top of the stack
534    Push30,
535    /// Places 31 byte item on top of the stack
536    Push31,
537    /// Places 32 byte item on top of the stack
538    Push32,
539    /// Duplicates the first stack item
540    Dup1,
541    /// Duplicates the 2nd stack item
542    Dup2,
543    /// Duplicates the 3rd stack item
544    Dup3,
545    /// Duplicates the 4th stack item
546    Dup4,
547    /// Duplicates the 5th stack item
548    Dup5,
549    /// Duplicates the 6th stack item
550    Dup6,
551    /// Duplicates the 7th stack item
552    Dup7,
553    /// Duplicates the 8th stack item
554    Dup8,
555    /// Duplicates the 9th stack item
556    Dup9,
557    /// Duplicates the 10th stack item
558    Dup10,
559    /// Duplicates the 11th stack item
560    Dup11,
561    /// Duplicates the 12th stack item
562    Dup12,
563    /// Duplicates the 13th stack item
564    Dup13,
565    /// Duplicates the 14th stack item
566    Dup14,
567    /// Duplicates the 15th stack item
568    Dup15,
569    /// Duplicates the 16th stack item
570    Dup16,
571    /// Duplicate the Nth stack item (17 <= N <= 235), with N given by an immediate byte
572    Dupn,
573    /// Exchange the top two stack items
574    Swap1,
575    /// Exchange the first and third stack items
576    Swap2,
577    /// Exchange the first and fourth stack items
578    Swap3,
579    /// Exchange the first and fifth stack items
580    Swap4,
581    /// Exchange the first and sixth stack items
582    Swap5,
583    /// Exchange the first and seventh stack items
584    Swap6,
585    /// Exchange the first and eighth stack items
586    Swap7,
587    /// Exchange the first and ninth stack items
588    Swap8,
589    /// Exchange the first and tenth stack items
590    Swap9,
591    /// Exchange the first and eleventh stack items
592    Swap10,
593    /// Exchange the first and twelfth stack items
594    Swap11,
595    /// Exchange the first and thirteenth stack items
596    Swap12,
597    /// Exchange the first and fourteenth stack items
598    Swap13,
599    /// Exchange the first and fifteenth stack items
600    Swap14,
601    /// Exchange the first and sixteenth stack items
602    Swap15,
603    /// Exchange the first and seventeenth stack items
604    Swap16,
605    /// Exchange the first and (N+1)th stack items (17 <= N <= 235), with N given by an immediate byte
606    Swapn,
607    /// Exchange the (N+1)th and (M+1)th stack items, with N and M given by an immediate byte
608    Exchange,
609    /// Append Log Record with no Topics
610    Log0,
611    /// Append Log Record with 1 Topic
612    Log1,
613    /// Append Log Record with 2 Topics
614    Log2,
615    /// Append Log Record with 3 Topics
616    Log3,
617    /// Append Log Record with 4 Topics
618    Log4,
619    /// Transaction-persistent, but storage-ephemeral variable load
620    Tload,
621    /// Transaction-persistent, but storage-ephemeral variable store
622    Tstore,
623    /// Copies an area of memory from src to dst. Areas can overlap.
624    Mcopy,
625    /// Create a new account with associated code
626    Create,
627    /// Message-call into an account
628    Call,
629    /// Message-call into this account with an alternative accounts code
630    Callcode,
631    /// Halt execution, returning output data
632    Return,
633    /// Message-call into this account with an alternative accounts code, persisting the sender and
634    /// value
635    Delegatecall,
636    /// Create a new account with associated code
637    Create2,
638    /// Static Message-call into an account
639    Staticcall,
640    /// Halt execution, reverting state changes, but returning data and remaining gas
641    Revert,
642    /// Invalid Instruction
643    Invalid,
644    /// Halt Execution and Register Account for later deletion
645    Selfdestruct,
646    /// Get hash of an account’s code
647    Extcodehash,
648}
649
650impl Opcode {
651    /// Translates an Opcode into a string
652    pub fn string(&self) -> String {
653        let opcode_str = match self {
654            Opcode::Stop => "00",
655            Opcode::Add => "01",
656            Opcode::Mul => "02",
657            Opcode::Sub => "03",
658            Opcode::Div => "04",
659            Opcode::Sdiv => "05",
660            Opcode::Mod => "06",
661            Opcode::Smod => "07",
662            Opcode::Addmod => "08",
663            Opcode::Mulmod => "09",
664            Opcode::Exp => "0a",
665            Opcode::Signextend => "0b",
666            Opcode::Lt => "10",
667            Opcode::Gt => "11",
668            Opcode::Slt => "12",
669            Opcode::Sgt => "13",
670            Opcode::Eq => "14",
671            Opcode::Iszero => "15",
672            Opcode::And => "16",
673            Opcode::Or => "17",
674            Opcode::Xor => "18",
675            Opcode::Not => "19",
676            Opcode::Byte => "1a",
677            Opcode::Shl => "1b",
678            Opcode::Shr => "1c",
679            Opcode::Sar => "1d",
680            Opcode::Keccak256 => "20",
681            Opcode::Address => "30",
682            Opcode::Balance => "31",
683            Opcode::Origin => "32",
684            Opcode::Caller => "33",
685            Opcode::Callvalue => "34",
686            Opcode::Calldataload => "35",
687            Opcode::Calldatasize => "36",
688            Opcode::Calldatacopy => "37",
689            Opcode::Codesize => "38",
690            Opcode::Codecopy => "39",
691            Opcode::Gasprice => "3a",
692            Opcode::Extcodesize => "3b",
693            Opcode::Extcodecopy => "3c",
694            Opcode::Returndatasize => "3d",
695            Opcode::Returndatacopy => "3e",
696            Opcode::Extcodehash => "3f",
697            Opcode::Blockhash => "40",
698            Opcode::Coinbase => "41",
699            Opcode::Timestamp => "42",
700            Opcode::Number => "43",
701            Opcode::Difficulty => "44",
702            Opcode::Prevrandao => "44",
703            Opcode::Gaslimit => "45",
704            Opcode::Chainid => "46",
705            Opcode::Clz => "1e",
706            Opcode::Selfbalance => "47",
707            Opcode::Basefee => "48",
708            Opcode::Blobhash => "49",
709            Opcode::Blobbasefee => "4a",
710            Opcode::Slotnum => "4b",
711            Opcode::Pop => "50",
712            Opcode::Mload => "51",
713            Opcode::Mstore => "52",
714            Opcode::Mstore8 => "53",
715            Opcode::Sload => "54",
716            Opcode::Sstore => "55",
717            Opcode::Jump => "56",
718            Opcode::Jumpi => "57",
719            Opcode::Pc => "58",
720            Opcode::Msize => "59",
721            Opcode::Gas => "5a",
722            Opcode::Jumpdest => "5b",
723            Opcode::Tload => "5c",
724            Opcode::Tstore => "5d",
725            Opcode::Mcopy => "5e",
726            Opcode::Push0 => "5f",
727            Opcode::Push1 => "60",
728            Opcode::Push2 => "61",
729            Opcode::Push3 => "62",
730            Opcode::Push4 => "63",
731            Opcode::Push5 => "64",
732            Opcode::Push6 => "65",
733            Opcode::Push7 => "66",
734            Opcode::Push8 => "67",
735            Opcode::Push9 => "68",
736            Opcode::Push10 => "69",
737            Opcode::Push11 => "6a",
738            Opcode::Push12 => "6b",
739            Opcode::Push13 => "6c",
740            Opcode::Push14 => "6d",
741            Opcode::Push15 => "6e",
742            Opcode::Push16 => "6f",
743            Opcode::Push17 => "70",
744            Opcode::Push18 => "71",
745            Opcode::Push19 => "72",
746            Opcode::Push20 => "73",
747            Opcode::Push21 => "74",
748            Opcode::Push22 => "75",
749            Opcode::Push23 => "76",
750            Opcode::Push24 => "77",
751            Opcode::Push25 => "78",
752            Opcode::Push26 => "79",
753            Opcode::Push27 => "7a",
754            Opcode::Push28 => "7b",
755            Opcode::Push29 => "7c",
756            Opcode::Push30 => "7d",
757            Opcode::Push31 => "7e",
758            Opcode::Push32 => "7f",
759            Opcode::Dup1 => "80",
760            Opcode::Dup2 => "81",
761            Opcode::Dup3 => "82",
762            Opcode::Dup4 => "83",
763            Opcode::Dup5 => "84",
764            Opcode::Dup6 => "85",
765            Opcode::Dup7 => "86",
766            Opcode::Dup8 => "87",
767            Opcode::Dup9 => "88",
768            Opcode::Dup10 => "89",
769            Opcode::Dup11 => "8a",
770            Opcode::Dup12 => "8b",
771            Opcode::Dup13 => "8c",
772            Opcode::Dup14 => "8d",
773            Opcode::Dup15 => "8e",
774            Opcode::Dup16 => "8f",
775            Opcode::Swap1 => "90",
776            Opcode::Swap2 => "91",
777            Opcode::Swap3 => "92",
778            Opcode::Swap4 => "93",
779            Opcode::Swap5 => "94",
780            Opcode::Swap6 => "95",
781            Opcode::Swap7 => "96",
782            Opcode::Swap8 => "97",
783            Opcode::Swap9 => "98",
784            Opcode::Swap10 => "99",
785            Opcode::Swap11 => "9a",
786            Opcode::Swap12 => "9b",
787            Opcode::Swap13 => "9c",
788            Opcode::Swap14 => "9d",
789            Opcode::Swap15 => "9e",
790            Opcode::Swap16 => "9f",
791            Opcode::Log0 => "a0",
792            Opcode::Log1 => "a1",
793            Opcode::Log2 => "a2",
794            Opcode::Log3 => "a3",
795            Opcode::Log4 => "a4",
796            Opcode::Dupn => "e6",
797            Opcode::Swapn => "e7",
798            Opcode::Exchange => "e8",
799            Opcode::Create => "f0",
800            Opcode::Call => "f1",
801            Opcode::Callcode => "f2",
802            Opcode::Return => "f3",
803            Opcode::Delegatecall => "f4",
804            Opcode::Create2 => "f5",
805            Opcode::Staticcall => "fa",
806            Opcode::Revert => "fd",
807            Opcode::Invalid => "fe",
808            Opcode::Selfdestruct => "ff",
809        };
810        opcode_str.to_string()
811    }
812
813    /// Returns true if the current opcode is a push opcode that takes a literal value
814    pub fn is_value_push(&self) -> bool {
815        matches!(
816            self,
817            Opcode::Push1
818                | Opcode::Push2
819                | Opcode::Push3
820                | Opcode::Push4
821                | Opcode::Push5
822                | Opcode::Push6
823                | Opcode::Push7
824                | Opcode::Push8
825                | Opcode::Push9
826                | Opcode::Push10
827                | Opcode::Push11
828                | Opcode::Push12
829                | Opcode::Push13
830                | Opcode::Push14
831                | Opcode::Push15
832                | Opcode::Push16
833                | Opcode::Push17
834                | Opcode::Push18
835                | Opcode::Push19
836                | Opcode::Push20
837                | Opcode::Push21
838                | Opcode::Push22
839                | Opcode::Push23
840                | Opcode::Push24
841                | Opcode::Push25
842                | Opcode::Push26
843                | Opcode::Push27
844                | Opcode::Push28
845                | Opcode::Push29
846                | Opcode::Push30
847                | Opcode::Push31
848                | Opcode::Push32
849        )
850    }
851
852    /// Prefixes the literal if necessary
853    pub fn prefix_push_literal(&self, literal: &str) -> String {
854        if self.is_value_push()
855            && let Ok(len) = u8::from_str_radix(&self.string(), 16)
856            && len >= 96
857        {
858            let size = (len - 96 + 1) * 2;
859            // This case should be caught in the parser
860            if literal.len() <= size as usize {
861                let zeros_needed = size - literal.len() as u8;
862                let zero_prefix = (0..zeros_needed).map(|_| "0").collect::<Vec<&str>>().join("");
863                return format!("{zero_prefix}{literal}");
864            }
865        }
866
867        literal.to_string()
868    }
869
870    /// Number of value bytes following a PUSH1..PUSH32 opcode, or `None` for any other opcode
871    pub fn push_size(&self) -> Option<usize> {
872        if !self.is_value_push() {
873            return None;
874        }
875        u8::from_str_radix(&self.string(), 16).ok().map(|byte| (byte - 0x5f) as usize)
876    }
877
878    /// Number of stack-position operands an opcode encodes in its immediate byte (EIP-8024)
879    ///
880    /// `dupn <n>` and `swapn <n>` take one operand, `exchange <n> <m>` takes two. Every other
881    /// opcode returns 0.
882    pub fn stack_immediate_operands(&self) -> usize {
883        match self {
884            Opcode::Dupn | Opcode::Swapn => 1,
885            Opcode::Exchange => 2,
886            _ => 0,
887        }
888    }
889
890    /// Returns true if the opcode is followed by a one-byte stack immediate (EIP-8024)
891    pub fn has_stack_immediate(&self) -> bool {
892        self.stack_immediate_operands() > 0
893    }
894
895    /// Encodes the stack operands of a DUPN, SWAPN or EXCHANGE into its immediate byte
896    ///
897    /// Operands use the same numbering as the EIP: `dupn 17` behaves like a `dup17`, `swapn 17`
898    /// like a `swap17`, and `exchange n m` swaps the (n+1)th and (m+1)th stack items.
899    ///
900    /// Returns `None` if the operands cannot be represented. The encoding never produces a byte
901    /// that would be read as JUMPDEST or PUSH1..PUSH32, so emitted code keeps its jump targets.
902    pub fn encode_stack_immediate(&self, operands: &[usize]) -> Option<u8> {
903        match (self, operands) {
904            (Opcode::Dupn | Opcode::Swapn, &[n]) => {
905                if !(STACK_IMMEDIATE_MIN_N..=STACK_IMMEDIATE_MAX_N).contains(&n) {
906                    return None;
907                }
908                let x = ((n + 256 - 145) % 256) as u8;
909                debug_assert_eq!(decode_single_immediate(x), Some(n));
910                Some(x)
911            }
912            (Opcode::Exchange, &[n, m]) => (0..=u8::MAX).find(|&x| decode_pair_immediate(x) == Some((n, m))),
913            _ => None,
914        }
915    }
916
917    /// Encodes the immediate data that follows the opcode from its resolved operand values
918    ///
919    /// PUSH1..PUSH32 take one value that must fit the push width; DUPN, SWAPN and EXCHANGE take
920    /// stack positions as described in [`Opcode::encode_stack_immediate`]. Returns the immediate
921    /// as a hex string, or a description of why the values cannot be encoded.
922    pub fn encode_immediate(&self, values: &[U256]) -> Result<String, String> {
923        if let Some(size) = self.push_size() {
924            let value = values[0];
925            if value.byte_len() > size {
926                return Err(format!("value {value:#x} does not fit into \"{self:?}\" ({size} bytes)"));
927            }
928            return Ok(hex::encode(&value.to_be_bytes::<32>()[32 - size..]));
929        }
930
931        let positions: Option<Vec<usize>> = values.iter().map(|v| usize::try_from(*v).ok()).collect();
932        positions.and_then(|p| self.encode_stack_immediate(&p)).map(|x| format!("{x:02x}")).ok_or_else(|| {
933            let got: Vec<String> = values.iter().map(U256::to_string).collect();
934            format!("{}, got {}", self.stack_immediate_hint(), got.join(" "))
935        })
936    }
937
938    /// Describes the valid operand range of a stack-immediate opcode, for error messages
939    pub fn stack_immediate_hint(&self) -> String {
940        match self {
941            Opcode::Dupn | Opcode::Swapn => {
942                format!("\"{self:?}\" takes one stack depth between {STACK_IMMEDIATE_MIN_N} and {STACK_IMMEDIATE_MAX_N}")
943            }
944            Opcode::Exchange => "\"Exchange\" takes two stack positions n and m with 1 <= n < m and n + m <= 30".to_string(),
945            _ => String::new(),
946        }
947    }
948
949    /// Checks if the value overflows the given push opcode
950    pub fn push_overflows(&self, literal: &str) -> bool {
951        if self.is_value_push()
952            && let Ok(len) = u8::from_str_radix(&self.string(), 16)
953            && len >= 96
954        {
955            let size = (len - 96 + 1) * 2;
956            return literal.len() > size as usize;
957        }
958
959        false
960    }
961
962    /// Returns the minimum EVM version required for this opcode
963    /// Returns None for opcodes that have been available since the beginning
964    pub fn requires_evm_version(&self) -> Option<SupportedEVMVersions> {
965        match self {
966            // Paris opcodes
967            Opcode::Prevrandao => Some(SupportedEVMVersions::Paris),
968            // Shanghai opcodes
969            Opcode::Push0 => Some(SupportedEVMVersions::Shanghai),
970            // Cancun opcodes
971            Opcode::Tload | Opcode::Tstore => Some(SupportedEVMVersions::Cancun),
972            Opcode::Mcopy => Some(SupportedEVMVersions::Cancun),
973            Opcode::Blobhash | Opcode::Blobbasefee => Some(SupportedEVMVersions::Cancun),
974            // Osaka opcodes
975            Opcode::Clz => Some(SupportedEVMVersions::Osaka),
976            // Amsterdam opcodes
977            Opcode::Slotnum | Opcode::Dupn | Opcode::Swapn | Opcode::Exchange => Some(SupportedEVMVersions::Amsterdam),
978            // All other opcodes are available since before Paris
979            _ => None,
980        }
981    }
982}
983
984/// Smallest stack depth reachable by DUPN and SWAPN
985pub const STACK_IMMEDIATE_MIN_N: usize = 17;
986/// Largest stack depth reachable by DUPN and SWAPN
987pub const STACK_IMMEDIATE_MAX_N: usize = 235;
988
989/// Decodes a DUPN/SWAPN immediate byte as defined by EIP-8024
990fn decode_single_immediate(x: u8) -> Option<usize> {
991    let x = x as usize;
992    if x <= 0x5a || x >= 0x80 { Some((x + 145) % 256) } else { None }
993}
994
995/// Decodes an EXCHANGE immediate byte into its `(n, m)` pair as defined by EIP-8024
996fn decode_pair_immediate(x: u8) -> Option<(usize, usize)> {
997    let x = x as usize;
998    if x > 0x51 && x < 0x80 {
999        return None;
1000    }
1001    let k = x ^ 0x8f;
1002    let (q, r) = (k / 16, k % 16);
1003    if q < r { Some((q + 1, r + 1)) } else { Some((r + 1, 29 - q)) }
1004}
1005
1006impl fmt::Display for Opcode {
1007    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1008        let opcode_str = self.string();
1009        write!(f, "{opcode_str}")
1010    }
1011}
1012
1013impl From<Opcode> for String {
1014    fn from(o: Opcode) -> Self {
1015        o.string()
1016    }
1017}
1018
1019#[cfg(test)]
1020mod tests {
1021    use super::*;
1022
1023    /// Test if the opcode array and map are in sync
1024    #[test]
1025    fn test_opcode_array_equal() {
1026        for opcode in OPCODES {
1027            assert!(OPCODES_MAP.contains_key(opcode), "{opcode}");
1028        }
1029        for opcode in OPCODES_MAP.keys() {
1030            assert!(OPCODES.contains(opcode), "{opcode}");
1031        }
1032    }
1033
1034    /// Byte values that would turn the immediate into a JUMPDEST or PUSH1..PUSH32
1035    fn is_forbidden_immediate(x: u8) -> bool {
1036        (0x5b..=0x7f).contains(&x)
1037    }
1038
1039    #[test]
1040    fn test_dupn_swapn_encoding_round_trips() {
1041        for opcode in [Opcode::Dupn, Opcode::Swapn] {
1042            for n in STACK_IMMEDIATE_MIN_N..=STACK_IMMEDIATE_MAX_N {
1043                let x = opcode.encode_stack_immediate(&[n]).unwrap_or_else(|| panic!("{opcode:?} {n} must be encodable"));
1044                assert!(!is_forbidden_immediate(x), "{opcode:?} {n} encoded to forbidden byte {x:#04x}");
1045                assert_eq!(decode_single_immediate(x), Some(n));
1046            }
1047            assert_eq!(opcode.encode_stack_immediate(&[0]), None);
1048            assert_eq!(opcode.encode_stack_immediate(&[16]), None);
1049            assert_eq!(opcode.encode_stack_immediate(&[236]), None);
1050            assert_eq!(opcode.encode_stack_immediate(&[17, 18]), None);
1051        }
1052    }
1053
1054    #[test]
1055    fn test_dupn_swapn_known_vectors() {
1056        // Reference values from EIP-8024
1057        assert_eq!(Opcode::Dupn.encode_stack_immediate(&[17]), Some(0x80));
1058        assert_eq!(Opcode::Dupn.encode_stack_immediate(&[144]), Some(0xff));
1059        assert_eq!(Opcode::Dupn.encode_stack_immediate(&[145]), Some(0x00));
1060        assert_eq!(Opcode::Swapn.encode_stack_immediate(&[235]), Some(0x5a));
1061    }
1062
1063    #[test]
1064    fn test_exchange_encoding_covers_all_valid_pairs() {
1065        let mut count = 0;
1066        for n in 1..30 {
1067            for m in (n + 1)..=(30 - n) {
1068                let x = Opcode::Exchange.encode_stack_immediate(&[n, m]).unwrap_or_else(|| panic!("exchange {n} {m} must be encodable"));
1069                assert!(!is_forbidden_immediate(x), "exchange {n} {m} encoded to forbidden byte {x:#04x}");
1070                assert_eq!(decode_pair_immediate(x), Some((n, m)));
1071                count += 1;
1072            }
1073        }
1074        // Every non-forbidden byte decodes to exactly one distinct pair
1075        let valid_bytes = (0..=u8::MAX).filter(|&x| decode_pair_immediate(x).is_some()).count();
1076        assert_eq!(count, valid_bytes);
1077
1078        // Reference value from EIP-8024: 0x8e swaps the second and third stack items
1079        assert_eq!(Opcode::Exchange.encode_stack_immediate(&[1, 2]), Some(0x8e));
1080
1081        assert_eq!(Opcode::Exchange.encode_stack_immediate(&[2, 1]), None);
1082        assert_eq!(Opcode::Exchange.encode_stack_immediate(&[1, 1]), None);
1083        assert_eq!(Opcode::Exchange.encode_stack_immediate(&[0, 5]), None);
1084        assert_eq!(Opcode::Exchange.encode_stack_immediate(&[14, 17]), None);
1085        assert_eq!(Opcode::Exchange.encode_stack_immediate(&[3]), None);
1086    }
1087
1088    #[test]
1089    fn test_stack_immediate_operands() {
1090        assert_eq!(Opcode::Dupn.stack_immediate_operands(), 1);
1091        assert_eq!(Opcode::Swapn.stack_immediate_operands(), 1);
1092        assert_eq!(Opcode::Exchange.stack_immediate_operands(), 2);
1093        assert!(!Opcode::Dup16.has_stack_immediate());
1094        assert!(!Opcode::Slotnum.has_stack_immediate());
1095        assert_eq!(Opcode::Dup16.encode_stack_immediate(&[17]), None);
1096    }
1097
1098    /// Validate that opcodes are ordered alphabetically with same prefix and then decreasing length
1099    #[test]
1100    fn test_opcode_order() {
1101        let mut sorted_opcodes = OPCODES.to_vec();
1102        sorted_opcodes.sort_by(|a, b| {
1103            // Find the common prefix length
1104            let common_len = a.chars().zip(b.chars()).take_while(|(c1, c2)| c1 == c2).count();
1105
1106            // If one string is a prefix of another, group them together and sort by length (descending)
1107            if common_len == a.len().min(b.len()) {
1108                b.len().cmp(&a.len()) // Longer string first
1109            } else {
1110                a.cmp(b) // Regular alphabetical sort
1111            }
1112        });
1113        assert_eq!(OPCODES.to_vec(), sorted_opcodes, "Opcodes are not ordered correctly");
1114    }
1115}