Skip to main content

example_custom_opcodes/
main.rs

1//! Custom opcodes example
2#![cfg_attr(not(test), warn(unused_crate_dependencies))]
3
4use revm::{
5    bytecode::opcode,
6    context::{Evm, TxEnv},
7    database::{BenchmarkDB, BENCH_TARGET},
8    handler::{instructions::EthInstructions, EthPrecompiles},
9    inspector::inspectors::TracerEip3155,
10    interpreter::{
11        interpreter::EthInterpreter,
12        interpreter_types::{Immediates, Jumps},
13        Instruction, InstructionContext,
14    },
15    primitives::hardfork::SpecId,
16    primitives::TxKind,
17    state::Bytecode,
18    Context, InspectEvm, MainContext,
19};
20
21/// Opcode hex value
22const MY_STATIC_JUMP: u8 = 0x0C;
23
24/// Demonstrates how to implement and use custom opcodes in REVM.
25/// This example shows how to create a custom static jump opcode that reads
26/// a 16-bit offset from the bytecode and performs a relative jump.
27pub fn main() {
28    let ctx = Context::mainnet().with_db(BenchmarkDB::new_bytecode(Bytecode::new_raw(
29        [
30            MY_STATIC_JUMP,
31            0x00,
32            0x03,
33            opcode::STOP,
34            opcode::JUMPDEST,
35            opcode::STOP,
36        ]
37        .into(),
38    )));
39
40    // Create a new instruction set with our mainnet opcodes.
41    let mut instructions = EthInstructions::new_mainnet_with_spec(SpecId::default());
42    // insert our custom opcode
43    instructions.insert_instruction(
44        MY_STATIC_JUMP,
45        Instruction::new(
46            |ctx: InstructionContext<'_, _, EthInterpreter>| {
47                let offset = ctx.interpreter.bytecode.read_i16();
48                ctx.interpreter.bytecode.relative_jump(offset as isize);
49            },
50            0,
51        ),
52    );
53
54    // Create a new EVM instance.
55    let mut evm = Evm::new(ctx, instructions, EthPrecompiles::new(SpecId::default()))
56        .with_inspector(TracerEip3155::new_stdout().without_summary());
57
58    // inspect the transaction.
59    let _ = evm.inspect_one_tx(
60        TxEnv::builder()
61            .kind(TxKind::Call(BENCH_TARGET))
62            .build()
63            .unwrap(),
64    );
65
66    // Expected output where we can see that JUMPDEST is called.
67    /*
68    "{"pc":0,"op":12,"gas":"0x1c97178","gasCost":"0x0","stack":[],"depth":1,"returnData":"0x","refund":"0x0","memSize":"0x0"}
69    {"pc":4,"op":91,"gas":"0x1c97178","gasCost":"0x1","stack":[],"depth":1,"returnData":"0x","refund":"0x0","memSize":"0x0","opName":"JUMPDEST"}
70    {"pc":5,"op":0,"gas":"0x1c97177","gasCost":"0x0","stack":[],"depth":1,"returnData":"0x","refund":"0x0","memSize":"0x0","opName":"STOP"}
71    */
72}