eva_asm/
opcode.rs

1//! EVM operation codes and mnemonics.
2
3use derive_more::{Binary, LowerHex, Octal, UpperHex};
4use strum::{Display, EnumCount, EnumIs, FromRepr};
5
6/// EVM operation code.
7#[derive(
8    Clone,
9    Copy,
10    PartialEq,
11    Eq,
12    PartialOrd,
13    Ord,
14    Debug,
15    derive_more::Display,
16    EnumIs,
17    LowerHex,
18    UpperHex,
19    Binary,
20    Octal,
21    Hash,
22)]
23pub enum OpCode {
24    /// A known opcode represented as a mnemonic.
25    Known(Mnemonic),
26    /// An unknown opcode represented as a byte.
27    Unknown(u8),
28}
29
30impl OpCode {
31    /// Convert a byte into an [`OpCode`], returning [`OpCode::Unknown`] if no known mnemonic
32    /// exists.
33    ///
34    /// # Example
35    /// ```
36    /// # use eva_asm::opcode::{OpCode, Mnemonic};
37    /// assert_eq!(OpCode::from_byte(0x5A), OpCode::Known(Mnemonic::GAS));
38    /// assert_eq!(OpCode::from_byte(0xF), OpCode::Unknown(0xF));
39    /// ```
40    #[must_use]
41    #[inline]
42    pub const fn from_byte(byte: u8) -> Self {
43        match Mnemonic::from_repr(byte) {
44            Some(mnemonic) => Self::Known(mnemonic),
45            None => Self::Unknown(byte),
46        }
47    }
48
49    /// Try to convert a byte into a known mnemonic.
50    /// Returns `None` if the opcode is unknown.
51    ///
52    /// # Example
53    /// ```
54    /// # use eva_asm::opcode::{OpCode, Mnemonic};
55    /// assert_eq!(OpCode::try_from_byte(0x5A), Some(OpCode::Known(Mnemonic::GAS)));
56    /// assert_eq!(OpCode::try_from_byte(0xF), None);
57    /// ```
58    #[must_use]
59    #[inline]
60    pub const fn try_from_byte(byte: u8) -> Option<Self> {
61        if let Some(mnemonic) = Mnemonic::from_repr(byte) {
62            Some(Self::Known(mnemonic))
63        } else {
64            None
65        }
66    }
67
68    /// Convert opcode into a byte.
69    ///
70    /// # Example
71    /// ```
72    /// # use eva_asm::opcode::{OpCode, Mnemonic};
73    /// assert_eq!(OpCode::Known(Mnemonic::GAS).into_byte(), 0x5A);
74    /// assert_eq!(OpCode::Unknown(0xF).into_byte(), 0xF);
75    /// ```
76    #[must_use]
77    #[inline]
78    pub const fn into_byte(self) -> u8 {
79        match self {
80            OpCode::Known(mnemonic) => mnemonic as u8,
81            OpCode::Unknown(byte) => byte,
82        }
83    }
84
85    /// Returns a value signifying whether this opcode is of the type `PUSHx`.
86    ///
87    /// # Example
88    /// ```
89    /// # use eva_asm::opcode::{OpCode, Mnemonic};
90    /// assert_eq!(OpCode::Known(Mnemonic::PUSH7).is_push(), true);
91    /// assert_eq!(OpCode::Known(Mnemonic::GAS).is_push(), false);
92    /// ```
93    #[must_use]
94    #[inline]
95    pub const fn is_push(&self) -> bool {
96        match self {
97            OpCode::Known(mnemonic) => mnemonic.is_push(),
98            OpCode::Unknown(_) => false,
99        }
100    }
101
102    /// Returns a value signifying whether this opcode is of the type `DUPx`.
103    ///
104    /// # Example
105    /// ```
106    /// # use eva_asm::opcode::{OpCode, Mnemonic};
107    /// assert_eq!(OpCode::Known(Mnemonic::DUP2).is_dup(), true);
108    /// assert_eq!(OpCode::Known(Mnemonic::GAS).is_dup(), false);
109    /// ```
110    #[must_use]
111    #[inline]
112    pub const fn is_dup(&self) -> bool {
113        match self {
114            OpCode::Known(mnemonic) => mnemonic.is_dup(),
115            OpCode::Unknown(_) => false,
116        }
117    }
118
119    /// Returns a value signifying whether this opcode is of the type `SWAPx`.
120    ///
121    /// # Example
122    /// ```
123    /// # use eva_asm::opcode::{OpCode, Mnemonic};
124    /// assert_eq!(OpCode::Known(Mnemonic::SWAP2).is_swap(), true);
125    /// assert_eq!(OpCode::Known(Mnemonic::GAS).is_swap(), false);
126    /// ```
127    #[must_use]
128    #[inline]
129    pub const fn is_swap(&self) -> bool {
130        match self {
131            OpCode::Known(mnemonic) => mnemonic.is_swap(),
132            OpCode::Unknown(_) => false,
133        }
134    }
135
136    /// Returns a value signifying whether this opcode is of the type `LOGx`.
137    ///
138    /// # Example
139    /// ```
140    /// # use eva_asm::opcode::{OpCode, Mnemonic};
141    /// assert_eq!(OpCode::Known(Mnemonic::LOG2).is_log(), true);
142    /// assert_eq!(OpCode::Known(Mnemonic::GAS).is_log(), false);
143    /// ```
144    #[must_use]
145    #[inline]
146    pub const fn is_log(&self) -> bool {
147        match self {
148            OpCode::Known(mnemonic) => mnemonic.is_log(),
149            OpCode::Unknown(_) => false,
150        }
151    }
152
153    /// Returns [`true`] for opcodes that terminate execution of the smart contract.
154    ///
155    /// # Example
156    /// ```
157    /// # use eva_asm::opcode::{OpCode, Mnemonic};
158    /// assert_eq!(OpCode::Known(Mnemonic::RETURN).is_terminator(), true);
159    /// assert_eq!(OpCode::Unknown(0xF).is_terminator(), true);
160    /// assert_eq!(OpCode::Known(Mnemonic::GAS).is_terminator(), false);
161    /// ```
162    #[must_use]
163    #[inline]
164    pub const fn is_terminator(&self) -> bool {
165        match self {
166            OpCode::Known(mnemonic) => mnemonic.is_terminator(),
167            OpCode::Unknown(_) => true,
168        }
169    }
170}
171
172impl PartialEq<Mnemonic> for OpCode {
173    #[inline]
174    fn eq(&self, other: &Mnemonic) -> bool {
175        u8::from(self) == *other as u8
176    }
177}
178
179impl PartialOrd<Mnemonic> for OpCode {
180    #[inline]
181    fn partial_cmp(&self, other: &Mnemonic) -> Option<std::cmp::Ordering> {
182        u8::from(self).partial_cmp(&(*other as u8))
183    }
184}
185
186impl PartialEq<u8> for OpCode {
187    #[inline]
188    fn eq(&self, other: &u8) -> bool {
189        u8::from(self).eq(other)
190    }
191}
192
193impl PartialOrd<u8> for OpCode {
194    #[inline]
195    fn partial_cmp(&self, other: &u8) -> Option<std::cmp::Ordering> {
196        u8::from(self).partial_cmp(other)
197    }
198}
199
200impl From<OpCode> for u8 {
201    #[inline]
202    fn from(opcode: OpCode) -> Self {
203        opcode.into_byte()
204    }
205}
206
207impl From<&OpCode> for u8 {
208    #[inline]
209    fn from(opcode: &OpCode) -> Self {
210        opcode.into_byte()
211    }
212}
213
214impl From<u8> for OpCode {
215    #[inline]
216    fn from(byte: u8) -> Self {
217        Self::from_byte(byte)
218    }
219}
220
221impl From<Mnemonic> for OpCode {
222    #[inline]
223    fn from(value: Mnemonic) -> Self {
224        Self::Known(value)
225    }
226}
227
228/// EVM operation code mnemonic.
229#[repr(u8)]
230#[non_exhaustive]
231#[derive(
232    Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Display, FromRepr, EnumIs, EnumCount, Hash,
233)]
234pub enum Mnemonic {
235    /// Halts execution.
236    STOP = 0x00,
237    /// Addition operation.
238    ADD = 0x01,
239    /// Multiplication operation.
240    MUL = 0x02,
241    /// Subtraction operation.
242    SUB = 0x03,
243    /// Integer division operation.
244    DIV = 0x04,
245    /// Signed integer division operation (truncated).
246    SDIV = 0x05,
247    /// Modulo remainder operation.
248    MOD = 0x06,
249    /// Signed modulo remainder operation.
250    SMOD = 0x07,
251    /// Modulo addition operation.
252    ADDMOD = 0x08,
253    /// Modulo multiplication operation.
254    MULMOD = 0x09,
255    /// Exponential operation.
256    EXP = 0x0A,
257    /// Extend length of two’s complement signed integer.
258    SIGNEXTEND = 0x0B,
259    /// Less-than comparison.
260    LT = 0x10,
261    /// Greater-than comparison.
262    GT = 0x11,
263    /// Signed less-than comparison.
264    SLT = 0x12,
265    /// Signed greater-than comparison.
266    SGT = 0x13,
267    /// Equality comparison.
268    EQ = 0x14,
269    /// Is-zero comparison.
270    ISZERO = 0x15,
271    /// Bitwise AND operation.
272    AND = 0x16,
273    /// Bitwise OR operation.
274    OR = 0x17,
275    /// Bitwise XOR operation.
276    XOR = 0x18,
277    /// Bitwise NOT operation.
278    NOT = 0x19,
279    /// Retrieve single byte from word.
280    BYTE = 0x1A,
281    /// Left shift operation.
282    SHL = 0x1B,
283    /// Logical right shift operation.
284    SHR = 0x1C,
285    /// Arithmetic (signed) right shift operation.
286    SAR = 0x1D,
287    /// Compute Keccak-256 hash.
288    KECCAK256 = 0x20,
289    /// Get address of currently executing account.
290    ADDRESS = 0x30,
291    /// Get balance of the given account.
292    BALANCE = 0x31,
293    /// Get execution origination address.
294    ORIGIN = 0x32,
295    /// Get caller address.
296    CALLER = 0x33,
297    /// Get deposited value by the instruction/transaction responsible for this execution.
298    CALLVALUE = 0x34,
299    /// Get input data of current environment.
300    CALLDATALOAD = 0x35,
301    /// Get size of input data in current environment.
302    CALLDATASIZE = 0x36,
303    /// Copy input data in current environment to memory.
304    CALLDATACOPY = 0x37,
305    /// Get size of code running in current environment.
306    CODESIZE = 0x38,
307    /// Copy code running in current environment to memory.
308    CODECOPY = 0x39,
309    /// Get price of gas in current environment.
310    GASPRICE = 0x3A,
311    /// Get size of an account’s code.
312    EXTCODESIZE = 0x3B,
313    /// Copy an account’s code to memory.
314    EXTCODECOPY = 0x3C,
315    /// Get size of output data from the previous call from the current environment.
316    RETURNDATASIZE = 0x3D,
317    /// Copy output data from the previous call to memory.
318    RETURNDATACOPY = 0x3E,
319    /// Get hash of an account’s code.
320    EXTCODEHASH = 0x3F,
321    /// Get the hash of one of the 256 most recent complete blocks.
322    BLOCKHASH = 0x40,
323    /// Get the block’s beneficiary address.
324    COINBASE = 0x41,
325    /// Get the block’s timestamp.
326    TIMESTAMP = 0x42,
327    /// Get the block’s number.
328    NUMBER = 0x43,
329    /// Get the block’s difficulty.
330    PREVRANDAO = 0x44,
331    /// Get the block’s gas limit.
332    GASLIMIT = 0x45,
333    /// Get the chain ID.
334    CHAINID = 0x46,
335    /// Get balance of currently executing account.
336    SELFBALANCE = 0x47,
337    /// Get the base fee.
338    BASEFEE = 0x48,
339    /// Get versioned hashes.
340    BLOBHASH = 0x49,
341    /// Returns the value of the blob base-fee of the current block.
342    BLOBBASEFEE = 0x4A,
343    /// Remove item from stack.
344    POP = 0x50,
345    /// Load word from memory.
346    MLOAD = 0x51,
347    /// Save word to memory.
348    MSTORE = 0x52,
349    /// Save byte to memory.
350    MSTORE8 = 0x53,
351    /// Load word from storage.
352    SLOAD = 0x54,
353    /// Save word to storage.
354    SSTORE = 0x55,
355    /// Alter the program counter.
356    JUMP = 0x56,
357    /// Conditionally alter the program counter.
358    JUMPI = 0x57,
359    /// Get the value of the program counter prior to the increment corresponding to this instruction.
360    PC = 0x58,
361    /// Get the size of active memory in bytes.
362    MSIZE = 0x59,
363    /// Get the amount of available gas, including the corresponding reduction for the cost of this instruction.
364    GAS = 0x5A,
365    /// Mark a valid destination for jumps.
366    JUMPDEST = 0x5B,
367    /// Load word from transient storage.
368    TLOAD = 0x5C,
369    /// Save word to transient storage.
370    TSTORE = 0x5D,
371    /// Copy memory areas.
372    MCOPY = 0x5E,
373    /// Place value 0 on stack.
374    PUSH0 = 0x5F,
375    /// Place 1 byte item on stack.
376    PUSH1 = 0x60,
377    /// Place 2 byte item on stack.
378    PUSH2 = 0x61,
379    /// Place 3 byte item on stack.
380    PUSH3 = 0x62,
381    /// Place 4 byte item on stack.
382    PUSH4 = 0x63,
383    /// Place 5 byte item on stack.
384    PUSH5 = 0x64,
385    /// Place 6 byte item on stack.
386    PUSH6 = 0x65,
387    /// Place 7 byte item on stack.
388    PUSH7 = 0x66,
389    /// Place 8 byte item on stack.
390    PUSH8 = 0x67,
391    /// Place 9 byte item on stack.
392    PUSH9 = 0x68,
393    /// Place 10 byte item on stack.
394    PUSH10 = 0x69,
395    /// Place 11 byte item on stack.
396    PUSH11 = 0x6A,
397    /// Place 12 byte item on stack.
398    PUSH12 = 0x6B,
399    /// Place 13 byte item on stack.
400    PUSH13 = 0x6C,
401    /// Place 14 byte item on stack.
402    PUSH14 = 0x6D,
403    /// Place 15 byte item on stack.
404    PUSH15 = 0x6E,
405    /// Place 16 byte item on stack.
406    PUSH16 = 0x6F,
407    /// Place 17 byte item on stack.
408    PUSH17 = 0x70,
409    /// Place 18 byte item on stack.
410    PUSH18 = 0x71,
411    /// Place 19 byte item on stack.
412    PUSH19 = 0x72,
413    /// Place 20 byte item on stack.
414    PUSH20 = 0x73,
415    /// Place 21 byte item on stack.
416    PUSH21 = 0x74,
417    /// Place 22 byte item on stack.
418    PUSH22 = 0x75,
419    /// Place 23 byte item on stack.
420    PUSH23 = 0x76,
421    /// Place 24 byte item on stack.
422    PUSH24 = 0x77,
423    /// Place 25 byte item on stack.
424    PUSH25 = 0x78,
425    /// Place 26 byte item on stack.
426    PUSH26 = 0x79,
427    /// Place 27 byte item on stack.
428    PUSH27 = 0x7A,
429    /// Place 28 byte item on stack.
430    PUSH28 = 0x7B,
431    /// Place 29 byte item on stack.
432    PUSH29 = 0x7C,
433    /// Place 30 byte item on stack.
434    PUSH30 = 0x7D,
435    /// Place 31 byte item on stack.
436    PUSH31 = 0x7E,
437    /// Place 32 byte (full word) item on stack.
438    PUSH32 = 0x7F,
439    /// Duplicate 1st stack item.
440    DUP1 = 0x80,
441    /// Duplicate 2nd stack item.
442    DUP2 = 0x81,
443    /// Duplicate 3rd stack item.
444    DUP3 = 0x82,
445    /// Duplicate 4th stack item.
446    DUP4 = 0x83,
447    /// Duplicate 5th stack item.
448    DUP5 = 0x84,
449    /// Duplicate 6th stack item.
450    DUP6 = 0x85,
451    /// Duplicate 7th stack item.
452    DUP7 = 0x86,
453    /// Duplicate 8th stack item.
454    DUP8 = 0x87,
455    /// Duplicate 9th stack item.
456    DUP9 = 0x88,
457    /// Duplicate 10th stack item.
458    DUP10 = 0x89,
459    /// Duplicate 11th stack item.
460    DUP11 = 0x8A,
461    /// Duplicate 12th stack item.
462    DUP12 = 0x8B,
463    /// Duplicate 13th stack item.
464    DUP13 = 0x8C,
465    /// Duplicate 14th stack item.
466    DUP14 = 0x8D,
467    /// Duplicate 15th stack item.
468    DUP15 = 0x8E,
469    /// Duplicate 16th stack item.
470    DUP16 = 0x8F,
471    /// Exchange 1st and 2nd stack items.
472    SWAP1 = 0x90,
473    /// Exchange 1st and 3rd stack items.
474    SWAP2 = 0x91,
475    /// Exchange 1st and 4th stack items.
476    SWAP3 = 0x92,
477    /// Exchange 1st and 5th stack items.
478    SWAP4 = 0x93,
479    /// Exchange 1st and 6th stack items.
480    SWAP5 = 0x94,
481    /// Exchange 1st and 7th stack items.
482    SWAP6 = 0x95,
483    /// Exchange 1st and 8th stack items.
484    SWAP7 = 0x96,
485    /// Exchange 1st and 9th stack items.
486    SWAP8 = 0x97,
487    /// Exchange 1st and 10th stack items.
488    SWAP9 = 0x98,
489    /// Exchange 1st and 11th stack items.
490    SWAP10 = 0x99,
491    /// Exchange 1st and 12th stack items.
492    SWAP11 = 0x9A,
493    /// Exchange 1st and 13th stack items.
494    SWAP12 = 0x9B,
495    /// Exchange 1st and 14th stack items.
496    SWAP13 = 0x9C,
497    /// Exchange 1st and 15th stack items.
498    SWAP14 = 0x9D,
499    /// Exchange 1st and 16th stack items.
500    SWAP15 = 0x9E,
501    /// Exchange 1st and 17th stack items.
502    SWAP16 = 0x9F,
503    /// Append log record with no topics.
504    LOG0 = 0xA0,
505    /// Append log record with one topic.
506    LOG1 = 0xA1,
507    /// Append log record with two topics.
508    LOG2 = 0xA2,
509    /// Append log record with three topics.
510    LOG3 = 0xA3,
511    /// Append log record with four topics.
512    LOG4 = 0xA4,
513    /// Create a new account with associated code.
514    CREATE = 0xF0,
515    /// Message-call into an account.
516    CALL = 0xF1,
517    /// Message-call into this account with alternative account’s code.
518    CALLCODE = 0xF2,
519    /// Halt execution returning output data.
520    RETURN = 0xF3,
521    /// Message-call into this account with an alternative account’s code, but persisting the current values for sender and value.
522    DELEGATECALL = 0xF4,
523    /// Create a new account with associated code at a predictable address.
524    CREATE2 = 0xF5,
525    /// Static message-call into an account.
526    STATICCALL = 0xFA,
527    /// Halt execution reverting state changes but returning data and remaining gas.
528    REVERT = 0xFD,
529    /// Designated invalid instruction.
530    INVALID = 0xFE,
531    /// Halt execution and register account for later deletion or send all Ether to address (post-Cancun).
532    SELFDESTRUCT = 0xFF,
533}
534
535impl Mnemonic {
536    /// Returns a value signifying whether this mnemonic is of the type `PUSHx`.
537    ///
538    /// # Example
539    /// ```
540    /// # use eva_asm::opcode::Mnemonic;
541    /// assert_eq!(Mnemonic::PUSH7.is_push(), true);
542    /// assert_eq!(Mnemonic::GAS.is_push(), false);
543    /// ```
544    #[must_use]
545    #[inline]
546    pub const fn is_push(&self) -> bool {
547        matches!(
548            self,
549            Self::PUSH0
550                | Self::PUSH1
551                | Self::PUSH2
552                | Self::PUSH3
553                | Self::PUSH4
554                | Self::PUSH5
555                | Self::PUSH6
556                | Self::PUSH7
557                | Self::PUSH8
558                | Self::PUSH9
559                | Self::PUSH10
560                | Self::PUSH11
561                | Self::PUSH12
562                | Self::PUSH13
563                | Self::PUSH14
564                | Self::PUSH15
565                | Self::PUSH16
566                | Self::PUSH17
567                | Self::PUSH18
568                | Self::PUSH19
569                | Self::PUSH20
570                | Self::PUSH21
571                | Self::PUSH22
572                | Self::PUSH23
573                | Self::PUSH24
574                | Self::PUSH25
575                | Self::PUSH26
576                | Self::PUSH27
577                | Self::PUSH28
578                | Self::PUSH29
579                | Self::PUSH30
580                | Self::PUSH31
581                | Self::PUSH32
582        )
583    }
584
585    /// Returns a value signifying whether this mnemonic is of the type `DUPx`.
586    ///
587    /// # Example
588    /// ```
589    /// # use eva_asm::opcode::Mnemonic;
590    /// assert_eq!(Mnemonic::DUP2.is_dup(), true);
591    /// assert_eq!(Mnemonic::GAS.is_dup(), false);
592    /// ```
593    #[must_use]
594    #[inline]
595    pub const fn is_dup(&self) -> bool {
596        matches!(
597            self,
598            Self::DUP1
599                | Self::DUP2
600                | Self::DUP3
601                | Self::DUP4
602                | Self::DUP5
603                | Self::DUP6
604                | Self::DUP7
605                | Self::DUP8
606                | Self::DUP9
607                | Self::DUP10
608                | Self::DUP11
609                | Self::DUP12
610                | Self::DUP13
611                | Self::DUP14
612                | Self::DUP15
613                | Self::DUP16
614        )
615    }
616
617    /// Returns a value signifying whether this mnemonic is of the type `SWAPx`.
618    ///
619    /// # Example
620    /// ```
621    /// # use eva_asm::opcode::Mnemonic;
622    /// assert_eq!(Mnemonic::SWAP2.is_swap(), true);
623    /// assert_eq!(Mnemonic::GAS.is_swap(), false);
624    /// ```
625    #[must_use]
626    #[inline]
627    pub const fn is_swap(&self) -> bool {
628        matches!(
629            self,
630            Self::SWAP1
631                | Self::SWAP2
632                | Self::SWAP3
633                | Self::SWAP4
634                | Self::SWAP5
635                | Self::SWAP6
636                | Self::SWAP7
637                | Self::SWAP8
638                | Self::SWAP9
639                | Self::SWAP10
640                | Self::SWAP11
641                | Self::SWAP12
642                | Self::SWAP13
643                | Self::SWAP14
644                | Self::SWAP15
645                | Self::SWAP16
646        )
647    }
648
649    /// Returns a value signifying whether this mnemonic is of the type `LOGx`.
650    ///
651    /// # Example
652    /// ```
653    /// # use eva_asm::opcode::Mnemonic;
654    /// assert_eq!(Mnemonic::LOG2.is_log(), true);
655    /// assert_eq!(Mnemonic::GAS.is_log(), false);
656    /// ```
657    #[must_use]
658    #[inline]
659    pub const fn is_log(&self) -> bool {
660        matches!(
661            self,
662            Self::LOG0 | Self::LOG1 | Self::LOG2 | Self::LOG3 | Self::LOG4
663        )
664    }
665
666    /// Returns [`true`] for mnemonics that terminate execution of the smart contract.
667    /// # Example
668    /// ```
669    /// # use eva_asm::opcode::Mnemonic;
670    /// assert_eq!(Mnemonic::STOP.is_terminator(), true);
671    /// assert_eq!(Mnemonic::REVERT.is_terminator(), true);
672    /// assert_eq!(Mnemonic::INVALID.is_terminator(), true);
673    /// assert_eq!(Mnemonic::GAS.is_terminator(), false);
674    /// ```
675    #[must_use]
676    #[inline]
677    pub const fn is_terminator(&self) -> bool {
678        matches!(
679            self,
680            Self::STOP | Self::RETURN | Self::REVERT | Self::INVALID | Self::SELFDESTRUCT
681        )
682    }
683}
684
685impl PartialEq<OpCode> for Mnemonic {
686    #[inline]
687    fn eq(&self, other: &OpCode) -> bool {
688        *self as u8 == u8::from(other)
689    }
690}
691
692impl PartialOrd<OpCode> for Mnemonic {
693    #[inline]
694    fn partial_cmp(&self, other: &OpCode) -> Option<std::cmp::Ordering> {
695        (*self as u8).partial_cmp(&u8::from(other))
696    }
697}
698
699impl PartialEq<u8> for Mnemonic {
700    #[inline]
701    fn eq(&self, other: &u8) -> bool {
702        *self as u8 == *other
703    }
704}
705
706impl PartialOrd<u8> for Mnemonic {
707    #[inline]
708    fn partial_cmp(&self, other: &u8) -> Option<std::cmp::Ordering> {
709        (*self as u8).partial_cmp(other)
710    }
711}
712
713/// Implement formatting for mnemonics.
714macro_rules! impl_mnemonic_fmt {
715    ($($fmt: ident),+) => {
716        $(impl std::fmt::$fmt for Mnemonic {
717            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
718                std::fmt::$fmt::fmt(&(*self as u8), f)
719            }
720        })+
721    };
722}
723
724impl_mnemonic_fmt!(LowerHex, UpperHex, Binary, Octal);
725
726#[cfg(test)]
727mod tests {
728    use super::*;
729
730    #[test]
731    fn mnemonic_fmt() {
732        let gas = Mnemonic::GAS;
733        assert_eq!(format!("{gas}"), "GAS");
734        assert_eq!(format!("{gas:x}"), "5a");
735        assert_eq!(format!("{gas:X}"), "5A");
736        assert_eq!(format!("{gas:b}"), "1011010");
737        assert_eq!(format!("{gas:o}"), "132");
738    }
739
740    #[test]
741    fn mnemonic_cmp() {
742        let gas = Mnemonic::GAS;
743        let add = Mnemonic::ADD;
744
745        assert_eq!(gas, gas);
746        assert_ne!(gas, add);
747        assert!(gas > add);
748        assert_eq!(gas, OpCode::Known(Mnemonic::GAS));
749        assert_ne!(gas, OpCode::Known(Mnemonic::ADD));
750        assert!(gas > OpCode::Known(Mnemonic::ADD));
751        assert_eq!(gas, 0x5A);
752        assert!(gas > 0x1);
753    }
754
755    #[test]
756    fn opcode_fmt() {
757        let gas = OpCode::Known(Mnemonic::GAS);
758        assert_eq!(format!("{gas}"), "GAS");
759        assert_eq!(format!("{gas:x}"), "5a");
760        assert_eq!(format!("{gas:X}"), "5A");
761        assert_eq!(format!("{gas:b}"), "1011010");
762        assert_eq!(format!("{gas:o}"), "132");
763
764        let unknown = OpCode::from(0xF);
765        assert_eq!(format!("{unknown}"), "15");
766        assert_eq!(format!("{unknown:x}"), "f");
767        assert_eq!(format!("{unknown:X}"), "F");
768        assert_eq!(format!("{unknown:b}"), "1111");
769        assert_eq!(format!("{unknown:o}"), "17");
770    }
771
772    #[test]
773    fn opcode_conversions() {
774        let gas = OpCode::Known(Mnemonic::GAS);
775
776        assert_eq!(u8::from(gas), 0x5A);
777        assert_eq!(u8::from(&gas), 0x5A);
778        assert_eq!(gas.into_byte(), 0x5A);
779        assert_eq!(gas, OpCode::from(Mnemonic::GAS));
780    }
781
782    #[test]
783    fn opcode_cmp() {
784        let gas = OpCode::Known(Mnemonic::GAS);
785        let add = OpCode::Known(Mnemonic::ADD);
786
787        assert_eq!(gas, gas);
788        assert!(gas > add);
789        assert_ne!(gas, add);
790        assert_eq!(gas, Mnemonic::GAS);
791        assert!(gas > Mnemonic::ADD);
792        assert_eq!(gas, 0x5A);
793        assert!(gas > 0x1);
794    }
795}