evm_disassembler/
types.rs

1//! Output types for Operation and Opcode
2use eyre::{eyre, Result};
3use std::fmt;
4
5/// A single EVM operation
6///
7/// For additional information on each operation see: https://www.evm.codes/
8#[derive(Clone, Copy, Debug, PartialEq, Eq)]
9#[allow(missing_docs)]
10pub enum Opcode {
11    STOP,
12    ADD,
13    MUL,
14    SUB,
15    DIV,
16    SDIV,
17    MOD,
18    SMOD,
19    ADDMOD,
20    MULMOD,
21    EXP,
22    SIGNEXTEND,
23    LT,
24    GT,
25    SLT,
26    SGT,
27    EQ,
28    ISZERO,
29    AND,
30    OR,
31    XOR,
32    NOT,
33    BYTE,
34    SHL,
35    SHR,
36    SAR,
37    SHA3,
38    ADDRESS,
39    BALANCE,
40    ORIGIN,
41    CALLER,
42    CALLVALUE,
43    CALLDATALOAD,
44    CALLDATASIZE,
45    CALLDATACOPY,
46    CODESIZE,
47    CODECOPY,
48    GASPRICE,
49    EXTCODESIZE,
50    EXTCODECOPY,
51    RETURNDATASIZE,
52    RETURNDATACOPY,
53    EXTCODEHASH,
54    BLOCKHASH,
55    COINBASE,
56    TIMESTAMP,
57    NUMBER,
58    DIFFICULTY,
59    GASLIMIT,
60    CHAINID,
61    SELFBALANCE,
62    BASEFEE,
63    POP,
64    MLOAD,
65    MSTORE,
66    MSTORE8,
67    SLOAD,
68    SSTORE,
69    JUMP,
70    JUMPI,
71    PC,
72    MSIZE,
73    GAS,
74    JUMPDEST,
75    MCOPY,
76    TLOAD,
77    TSTORE,
78    PUSH0,
79    PUSH1,
80    PUSH2,
81    PUSH3,
82    PUSH4,
83    PUSH5,
84    PUSH6,
85    PUSH7,
86    PUSH8,
87    PUSH9,
88    PUSH10,
89    PUSH11,
90    PUSH12,
91    PUSH13,
92    PUSH14,
93    PUSH15,
94    PUSH16,
95    PUSH17,
96    PUSH18,
97    PUSH19,
98    PUSH20,
99    PUSH21,
100    PUSH22,
101    PUSH23,
102    PUSH24,
103    PUSH25,
104    PUSH26,
105    PUSH27,
106    PUSH28,
107    PUSH29,
108    PUSH30,
109    PUSH31,
110    PUSH32,
111    DUP1,
112    DUP2,
113    DUP3,
114    DUP4,
115    DUP5,
116    DUP6,
117    DUP7,
118    DUP8,
119    DUP9,
120    DUP10,
121    DUP11,
122    DUP12,
123    DUP13,
124    DUP14,
125    DUP15,
126    DUP16,
127    SWAP1,
128    SWAP2,
129    SWAP3,
130    SWAP4,
131    SWAP5,
132    SWAP6,
133    SWAP7,
134    SWAP8,
135    SWAP9,
136    SWAP10,
137    SWAP11,
138    SWAP12,
139    SWAP13,
140    SWAP14,
141    SWAP15,
142    SWAP16,
143    LOG0,
144    LOG1,
145    LOG2,
146    LOG3,
147    LOG4,
148    CREATE,
149    CALL,
150    CALLCODE,
151    RETURN,
152    DELEGATECALL,
153    CREATE2,
154    STATICCALL,
155    REVERT,
156    INVALID,
157    SELFDESTRUCT,
158    BLOBBASEFEE,
159    BLOBHASH,
160    CLZ,
161    // EOF Data Section (EIP-7480)
162    DATALOAD,
163    DATALOADN,
164    DATASIZE,
165    DATACOPY,
166    // EOF Static Jumps (EIP-4200)
167    RJUMP,
168    RJUMPI,
169    RJUMPV,
170    // EOF Functions (EIP-4750, EIP-6206)
171    CALLF,
172    RETF,
173    JUMPF,
174    // EOF Stack (EIP-663)
175    DUPN,
176    SWAPN,
177    EXCHANGE,
178    // EOF Type (EIP-7761)
179    EXTCODETYPE,
180    // EOF Contract Creation (EIP-7620, EIP-7873)
181    EOFCREATE,
182    TXCREATE,
183    RETURNCONTRACT,
184    // EOF Calls (EIP-7069)
185    RETURNDATALOAD,
186    EXTCALL,
187    EXTDELEGATECALL,
188    EXTSTATICCALL,
189    // Other (EIP-5920)
190    PAY,
191}
192
193impl Opcode {
194    /// Convert a byte into an Opcode
195    pub fn from_byte(byte: u8) -> Opcode {
196        match byte {
197            0x00 => Opcode::STOP,
198            0x01 => Opcode::ADD,
199            0x02 => Opcode::MUL,
200            0x03 => Opcode::SUB,
201            0x04 => Opcode::DIV,
202            0x05 => Opcode::SDIV,
203            0x06 => Opcode::MOD,
204            0x07 => Opcode::SMOD,
205            0x08 => Opcode::ADDMOD,
206            0x09 => Opcode::MULMOD,
207            0x0A => Opcode::EXP,
208            0x0B => Opcode::SIGNEXTEND,
209            0x10 => Opcode::LT,
210            0x11 => Opcode::GT,
211            0x12 => Opcode::SLT,
212            0x13 => Opcode::SGT,
213            0x14 => Opcode::EQ,
214            0x15 => Opcode::ISZERO,
215            0x16 => Opcode::AND,
216            0x17 => Opcode::OR,
217            0x18 => Opcode::XOR,
218            0x19 => Opcode::NOT,
219            0x1a => Opcode::BYTE,
220            0x1b => Opcode::SHL,
221            0x1c => Opcode::SHR,
222            0x1d => Opcode::SAR,
223            0x1e => Opcode::CLZ,
224            0x20 => Opcode::SHA3,
225            0x30 => Opcode::ADDRESS,
226            0x31 => Opcode::BALANCE,
227            0x32 => Opcode::ORIGIN,
228            0x33 => Opcode::CALLER,
229            0x34 => Opcode::CALLVALUE,
230            0x35 => Opcode::CALLDATALOAD,
231            0x36 => Opcode::CALLDATASIZE,
232            0x37 => Opcode::CALLDATACOPY,
233            0x38 => Opcode::CODESIZE,
234            0x39 => Opcode::CODECOPY,
235            0x3a => Opcode::GASPRICE,
236            0x3b => Opcode::EXTCODESIZE,
237            0x3c => Opcode::EXTCODECOPY,
238            0x3d => Opcode::RETURNDATASIZE,
239            0x3e => Opcode::RETURNDATACOPY,
240            0x3f => Opcode::EXTCODEHASH,
241            0x40 => Opcode::BLOCKHASH,
242            0x41 => Opcode::COINBASE,
243            0x42 => Opcode::TIMESTAMP,
244            0x43 => Opcode::NUMBER,
245            0x44 => Opcode::DIFFICULTY,
246            0x45 => Opcode::GASLIMIT,
247            0x46 => Opcode::CHAINID,
248            0x47 => Opcode::SELFBALANCE,
249            0x48 => Opcode::BASEFEE,
250            0x49 => Opcode::BLOBHASH,
251            0x4a => Opcode::BLOBBASEFEE,
252            0x50 => Opcode::POP,
253            0x51 => Opcode::MLOAD,
254            0x52 => Opcode::MSTORE,
255            0x53 => Opcode::MSTORE8,
256            0x54 => Opcode::SLOAD,
257            0x55 => Opcode::SSTORE,
258            0x56 => Opcode::JUMP,
259            0x57 => Opcode::JUMPI,
260            0x58 => Opcode::PC,
261            0x59 => Opcode::MSIZE,
262            0x5a => Opcode::GAS,
263            0x5b => Opcode::JUMPDEST,
264            0x5c => Opcode::TLOAD,
265            0x5d => Opcode::TSTORE,
266            0x5e => Opcode::MCOPY,
267            0x5F => Opcode::PUSH0,
268            0x60 => Opcode::PUSH1,
269            0x61 => Opcode::PUSH2,
270            0x62 => Opcode::PUSH3,
271            0x63 => Opcode::PUSH4,
272            0x64 => Opcode::PUSH5,
273            0x65 => Opcode::PUSH6,
274            0x66 => Opcode::PUSH7,
275            0x67 => Opcode::PUSH8,
276            0x68 => Opcode::PUSH9,
277            0x69 => Opcode::PUSH10,
278            0x6a => Opcode::PUSH11,
279            0x6b => Opcode::PUSH12,
280            0x6c => Opcode::PUSH13,
281            0x6d => Opcode::PUSH14,
282            0x6e => Opcode::PUSH15,
283            0x6f => Opcode::PUSH16,
284            0x70 => Opcode::PUSH17,
285            0x71 => Opcode::PUSH18,
286            0x72 => Opcode::PUSH19,
287            0x73 => Opcode::PUSH20,
288            0x74 => Opcode::PUSH21,
289            0x75 => Opcode::PUSH22,
290            0x76 => Opcode::PUSH23,
291            0x77 => Opcode::PUSH24,
292            0x78 => Opcode::PUSH25,
293            0x79 => Opcode::PUSH26,
294            0x7a => Opcode::PUSH27,
295            0x7b => Opcode::PUSH28,
296            0x7c => Opcode::PUSH29,
297            0x7d => Opcode::PUSH30,
298            0x7e => Opcode::PUSH31,
299            0x7f => Opcode::PUSH32,
300            0x80 => Opcode::DUP1,
301            0x81 => Opcode::DUP2,
302            0x82 => Opcode::DUP3,
303            0x83 => Opcode::DUP4,
304            0x84 => Opcode::DUP5,
305            0x85 => Opcode::DUP6,
306            0x86 => Opcode::DUP7,
307            0x87 => Opcode::DUP8,
308            0x88 => Opcode::DUP9,
309            0x89 => Opcode::DUP10,
310            0x8a => Opcode::DUP11,
311            0x8b => Opcode::DUP12,
312            0x8c => Opcode::DUP13,
313            0x8d => Opcode::DUP14,
314            0x8e => Opcode::DUP15,
315            0x8f => Opcode::DUP16,
316            0x90 => Opcode::SWAP1,
317            0x91 => Opcode::SWAP2,
318            0x92 => Opcode::SWAP3,
319            0x93 => Opcode::SWAP4,
320            0x94 => Opcode::SWAP5,
321            0x95 => Opcode::SWAP6,
322            0x96 => Opcode::SWAP7,
323            0x97 => Opcode::SWAP8,
324            0x98 => Opcode::SWAP9,
325            0x99 => Opcode::SWAP10,
326            0x9a => Opcode::SWAP11,
327            0x9b => Opcode::SWAP12,
328            0x9c => Opcode::SWAP13,
329            0x9d => Opcode::SWAP14,
330            0x9e => Opcode::SWAP15,
331            0x9f => Opcode::SWAP16,
332            0xa0 => Opcode::LOG0,
333            0xa1 => Opcode::LOG1,
334            0xa2 => Opcode::LOG2,
335            0xa3 => Opcode::LOG3,
336            0xa4 => Opcode::LOG4,
337            0xf0 => Opcode::CREATE,
338            0xf1 => Opcode::CALL,
339            0xf2 => Opcode::CALLCODE,
340            0xf3 => Opcode::RETURN,
341            0xf4 => Opcode::DELEGATECALL,
342            0xf5 => Opcode::CREATE2,
343            0xfa => Opcode::STATICCALL,
344            0xfd => Opcode::REVERT,
345            0xff => Opcode::SELFDESTRUCT,
346            _ => Opcode::INVALID,
347        }
348    }
349
350    /// Convert a byte into an Opcode (EOF-aware version)
351    ///
352    /// This version includes EOF opcodes and should only be used for EOF containers.
353    pub fn from_byte_eof(byte: u8) -> Opcode {
354        match byte {
355            // EOF Data Section (EIP-7480)
356            0xd0 => Opcode::DATALOAD,
357            0xd1 => Opcode::DATALOADN,
358            0xd2 => Opcode::DATASIZE,
359            0xd3 => Opcode::DATACOPY,
360            // EOF Static Jumps (EIP-4200)
361            0xe0 => Opcode::RJUMP,
362            0xe1 => Opcode::RJUMPI,
363            0xe2 => Opcode::RJUMPV,
364            // EOF Functions (EIP-4750, EIP-6206)
365            0xe3 => Opcode::CALLF,
366            0xe4 => Opcode::RETF,
367            0xe5 => Opcode::JUMPF,
368            // EOF Stack (EIP-663)
369            0xe6 => Opcode::DUPN,
370            0xe7 => Opcode::SWAPN,
371            0xe8 => Opcode::EXCHANGE,
372            // EOF Type (EIP-7761)
373            0xe9 => Opcode::EXTCODETYPE,
374            // EOF Contract Creation (EIP-7620, EIP-7873)
375            0xec => Opcode::EOFCREATE,
376            0xed => Opcode::TXCREATE,
377            0xee => Opcode::RETURNCONTRACT,
378            // EOF Calls (EIP-7069)
379            0xf7 => Opcode::RETURNDATALOAD,
380            0xf8 => Opcode::EXTCALL,
381            0xf9 => Opcode::EXTDELEGATECALL,
382            0xfb => Opcode::EXTSTATICCALL,
383            // Other (EIP-5920)
384            0xfc => Opcode::PAY,
385            // Fall back to legacy opcodes for non-EOF-specific bytes
386            _ => Self::from_byte(byte),
387        }
388    }
389}
390
391/// A decoded operation
392///
393/// An operation is represented by the combination of an opcode, the offset in the bytecode and any
394/// additional bytes that are part of the operation (only for PUSH operations).
395#[derive(PartialEq, Eq)]
396pub struct Operation {
397    /// The opcode
398    pub opcode: Opcode,
399    /// Additional bytes that are part of the Operation (only for PUSH)
400    pub input: Vec<u8>,
401    /// The offset in the bytecode
402    pub offset: u32,
403}
404
405impl fmt::Debug for Operation {
406    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
407        let mut formatted = format!(
408            "{:0>8}: {:?}",
409            format!("{:#x}", self.offset).trim_start_matches("0x"),
410            self.opcode
411        );
412        if !self.input.is_empty() {
413            let encoded_bytes = hex::encode(&self.input);
414            let mut formatted_bytes = encoded_bytes.trim_start_matches('0');
415            if formatted_bytes.is_empty() {
416                formatted_bytes = "0";
417            }
418            formatted = format!("{} {}", formatted, "0x".to_owned() + formatted_bytes);
419        }
420        write!(f, "{formatted}")
421    }
422}
423
424impl Operation {
425    /// Creates a new operation with empty `input` bytes
426    pub fn new(opcode: Opcode, offset: u32) -> Self {
427        Operation {
428            opcode,
429            offset,
430            input: Vec::new(),
431        }
432    }
433
434    /// Adds additional bytes to the operation (for PUSH instructions)
435    pub fn with_bytes(
436        self,
437        num_bytes: u8,
438        bytes: &mut dyn ExactSizeIterator<Item = u8>,
439    ) -> Result<Self> {
440        if num_bytes == 0 {
441            return Ok(self);
442        }
443        if num_bytes as usize > bytes.len() {
444            return Err(eyre!(
445                "Not enough bytes to read - expected {} but only {} left",
446                num_bytes,
447                bytes.len()
448            ));
449        }
450        Ok(Operation {
451            opcode: self.opcode,
452            offset: self.offset,
453            input: bytes.take(num_bytes as usize).collect(),
454        })
455    }
456}