vyper_rs/
vyper.rs

1//! This is the main module of the crate. Uses the global installation of Vyper.
2
3use crate::{
4    utils::{self, get_contracts_in_dir},
5    vyper_errors::VyperErrors,
6};
7use serde::{Deserialize, Serialize};
8use serde_json::{to_writer_pretty, Value};
9use std::{
10    borrow::BorrowMut,
11    fmt::Display,
12    fs::File,
13    io::{BufWriter, Write},
14    path::{Path, PathBuf},
15    process::Command,
16    sync::Arc,
17    thread,
18};
19use tokio::task::JoinHandle;
20
21/// Represents important information about a Vyper contract. ABI doesn't need to point to an
22/// existing file since it can just be generated using `gen_abi()`. If the ABI already exists at the given path, you can use serde_json to retrieve it from a file.
23#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
24pub struct Vyper<'a> {
25    pub path_to_code: &'a Path,
26    pub bytecode: Option<String>,
27    pub abi: PathBuf,
28    pub venv: Option<&'a Path>,
29}
30
31impl<'a> Display for Vyper<'a> {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        write!(
34            f,
35            "\nRoot path: {:?}, \nContract Bytecode: {:?}, \nContract Abi: {:?}",
36            self.path_to_code, self.bytecode, self.abi
37        )
38    }
39}
40
41impl<'a> Vyper<'a> {
42    /// Constructor function that takes in the path to your vyper contract
43    pub fn new(path: &'a Path) -> Self {
44        let np = path.with_extension("json");
45        Self {
46            path_to_code: path,
47            bytecode: None,
48            abi: np,
49            venv: None,
50        }
51    }
52
53    pub fn with_abi(root: &'a Path, abi_path: PathBuf) -> Self {
54        Self {
55            path_to_code: root,
56            bytecode: None,
57            abi: abi_path,
58            venv: None,
59        }
60    }
61
62    pub fn with_venv(path: &'a Path, venv: &'a Path) -> Vyper<'a> {
63        let abi = path.with_extension("json");
64
65        Vyper {
66            path_to_code: path,
67            bytecode: None,
68            abi,
69            venv: Some(venv),
70        }
71    }
72
73    pub fn with_venv_and_abi(path: &'a Path, venv: &'a Path, abi: PathBuf) -> Vyper<'a> {
74        Vyper {
75            path_to_code: path,
76            bytecode: None,
77            abi,
78            venv: Some(venv),
79        }
80    }
81
82    pub fn abi_mut(&mut self) -> &mut PathBuf {
83        self.abi.borrow_mut()
84    }
85
86    pub fn abi_exists(&self) -> bool {
87        self.abi.exists()
88    }
89
90    pub fn contract_exists(&self) -> bool {
91        self.path_to_code.exists()
92    }
93
94    pub fn get_vyper(&self) -> String {
95        if let Some(venv) = self.venv {
96            if cfg!(target_os = "windows") {
97                format!("{}/scripts/vyper", venv.to_string_lossy())
98            } else {
99                format!("{}/bin/vyper", venv.to_string_lossy())
100            }
101        } else {
102            "vyper".to_owned()
103        }
104    }
105
106    pub fn get_pip(&self) -> String {
107        if let Some(venv) = self.venv {
108            if cfg!(target_os = "windows") {
109                format!("{}/scripts/pip3", venv.to_string_lossy())
110            } else {
111                format!("{}/bin/pip3", venv.to_string_lossy())
112            }
113        } else {
114            "pip3".to_owned()
115        }
116    }
117
118    pub fn exists(&self) -> bool {
119        Command::new(self.get_vyper()).arg("-h").output().is_ok()
120    }
121
122    /// check the version of the vyper compiler
123    pub fn get_version(&self) -> Result<String, VyperErrors> {
124        let out = Command::new(self.get_vyper()).arg("--version").output()?;
125        if !out.status.success() {
126            Err(VyperErrors::CompilerError(
127                "Couldn't locate version info, installation does not exist".to_string(),
128            ))?
129        }
130        Ok(String::from_utf8_lossy(&out.stdout).to_string())
131    }
132
133    /// Compiles a vyper contract by invoking the vyper compiler, updates the ABI field in the Vyper struct
134    pub fn compile(&mut self) -> Result<(), VyperErrors> {
135        let compiler_output = Command::new(self.get_vyper())
136            .arg(self.path_to_code)
137            .output()?;
138        if compiler_output.status.success() {
139            let mut out = String::from_utf8_lossy(&compiler_output.stdout).to_string();
140            for _ in 0..1 {
141                out.pop();
142            }
143            if !out.starts_with("0x") {
144                self.bytecode = out.split(":").last().map(|s| s.to_owned());
145            } else {
146                self.bytecode = Some(out);
147            }
148
149            Ok(())
150        } else {
151            Err(VyperErrors::CompilerError(
152                String::from_utf8_lossy(&compiler_output.stderr).to_string(),
153            ))?
154        }
155    }
156
157    pub fn compile_blueprint(&mut self) -> Result<(), VyperErrors> {
158        let compiler_output = Command::new(self.get_vyper())
159            .arg("-f")
160            .arg("blueprint_bytecode")
161            .arg(self.path_to_code)
162            .output()?;
163        if compiler_output.status.success() {
164            let mut out = String::from_utf8_lossy(&compiler_output.stdout).to_string();
165            for _ in 0..1 {
166                out.pop();
167            }
168            if !out.starts_with("0x") {
169                self.bytecode = out.split(":").last().map(|s| s.to_owned());
170            } else {
171                self.bytecode = Some(out);
172            }
173            Ok(())
174        } else {
175            Err(VyperErrors::CompilerError(
176                String::from_utf8_lossy(&compiler_output.stderr).to_string(),
177            ))?
178        }
179    }
180
181    /// Compiles a vyper contract by invoking the vyper compiler, arg for specifying the EVM version to compile to
182    pub fn compile_ver(&mut self, ver: &Evm) -> Result<(), VyperErrors> {
183        let compiler_output = Command::new(self.get_vyper())
184            .arg(self.path_to_code)
185            .arg("--evm-version")
186            .arg(ver.to_string())
187            .output()?;
188
189        if compiler_output.status.success() {
190            let mut out = String::from_utf8_lossy(&compiler_output.stdout).to_string();
191            for _ in 0..1 {
192                out.pop();
193            }
194            if !out.starts_with("0x") {
195                self.bytecode = out.split(":").last().map(|s| s.to_owned());
196            } else {
197                self.bytecode = Some(out);
198            }
199            Ok(())
200        } else {
201            Err(VyperErrors::CompilerError(
202                String::from_utf8_lossy(&compiler_output.stderr).to_string(),
203            ))?
204        }
205    }
206    /// Generates the ABI and creates a file @ the abi path specified in the Vyper struct
207    pub fn gen_abi(&self) -> Result<(), VyperErrors> {
208        let compiler_output = Command::new(self.get_vyper())
209            .arg("-f")
210            .arg("abi")
211            .arg(self.path_to_code)
212            .output()?;
213
214        if compiler_output.status.success() {
215            let json = serde_json::from_str::<Value>(&String::from_utf8_lossy(
216                &compiler_output.stdout,
217            ))?;
218
219            let file = File::create(&self.abi)?;
220
221            to_writer_pretty(file, &json)?;
222            Ok(())
223        } else {
224            Err(VyperErrors::CompilerError(
225                String::from_utf8_lossy(&compiler_output.stderr).to_string(),
226            ))?
227        }
228    }
229
230    /// Generates the ABI and creates a file @ the abi path specified in the Vyper struct
231    pub fn get_abi(&self) -> Result<Value, VyperErrors> {
232        let compiler_output = Command::new(self.get_vyper())
233            .arg("-f")
234            .arg("abi")
235            .arg(self.path_to_code)
236            .output()?;
237
238        if compiler_output.status.success() {
239            let json = serde_json::from_str::<Value>(&String::from_utf8_lossy(
240                &compiler_output.stdout,
241            ))?;
242            Ok(json)
243        } else {
244            Err(VyperErrors::CompilerError(
245                String::from_utf8_lossy(&compiler_output.stderr).to_string(),
246            ))?
247        }
248    }
249
250    /// Storage layout as JSON, saves it to a file
251    pub fn storage_layout(&self) -> Result<(), VyperErrors> {
252        let compiler_output = Command::new(self.get_vyper())
253            .arg("-f")
254            .arg("layout")
255            .arg(self.path_to_code.to_string_lossy().to_string())
256            .output()?;
257
258        if compiler_output.status.success() {
259            let json = serde_json::from_str::<Value>(&String::from_utf8_lossy(
260                &compiler_output.stdout,
261            ))?;
262            let file = File::create("./storage_layout.json")?;
263            to_writer_pretty(file, &json)?;
264            Ok(())
265        } else {
266            Err(VyperErrors::CompilerError(
267                String::from_utf8_lossy(&compiler_output.stderr).to_string(),
268            ))?
269        }
270    }
271    /// AST of your contract as JSON, saves it to a file
272    pub fn ast(&self) -> Result<(), VyperErrors> {
273        let compiler_output = Command::new(self.get_vyper())
274            .arg("-f")
275            .arg("ast")
276            .arg(self.path_to_code.to_string_lossy().to_string())
277            .output()?;
278
279        if compiler_output.status.success() {
280            let json = serde_json::from_str::<Value>(&String::from_utf8_lossy(
281                &compiler_output.stdout,
282            ))?;
283            let file: File = File::create("./ast.json")?;
284            to_writer_pretty(file, &json)?;
285            Ok(())
286        } else {
287            Err(VyperErrors::CompilerError(
288                String::from_utf8_lossy(&compiler_output.stderr).to_string(),
289            ))?
290        }
291    }
292    /// Generates an external interface for your vyper contract to be called with
293    pub fn interface(&self) -> Result<(), VyperErrors> {
294        let compiler_output = Command::new(self.get_vyper())
295            .arg("-f")
296            .arg("external_interface")
297            .arg(self.path_to_code.to_string_lossy().to_string())
298            .output()?;
299        if compiler_output.status.success() {
300            let mut buffer = BufWriter::new(File::create("./interface.vy")?);
301            buffer.write_all(&compiler_output.stdout)?;
302            Ok(())
303        } else {
304            Err(VyperErrors::CompilerError(
305                String::from_utf8_lossy(&compiler_output.stderr).to_string(),
306            ))?
307        }
308    }
309    /// Generates the opcodes produced by your vyper contract, saves it as a text file
310    pub fn opcodes(&self) -> Result<(), VyperErrors> {
311        let compiler_output = Command::new(self.get_vyper())
312            .arg("-f")
313            .arg("opcodes")
314            .arg(self.path_to_code.to_string_lossy().to_string())
315            .output()?;
316
317        if compiler_output.status.success() {
318            let mut buffer = BufWriter::new(File::create("./opcodes.txt")?);
319            buffer.write_all(&compiler_output.stdout)?;
320            Ok(())
321        } else {
322            Err(VyperErrors::CompilerError(
323                String::from_utf8_lossy(&compiler_output.stderr).to_string(),
324            ))?
325        }
326    }
327    /// Generates the opcodes produced by your vyper contract at runtime, saves it as a text file
328    pub fn opcodes_runtime(&self) -> Result<(), VyperErrors> {
329        let compiler_output = Command::new(self.get_vyper())
330            .arg("-f")
331            .arg("opcodes_runtime")
332            .arg(self.path_to_code.to_string_lossy().to_string())
333            .output()?;
334
335        if compiler_output.status.success() {
336            let mut buffer = BufWriter::new(File::create("./opcodes_runtime.txt")?);
337            buffer.write_all(&compiler_output.stdout)?;
338            Ok(())
339        } else {
340            Err(VyperErrors::CompilerError(
341                String::from_utf8_lossy(&compiler_output.stderr).to_string(),
342            ))?
343        }
344    }
345    /// Natspec user documentation for vyper contract
346    pub fn userdoc(&self) -> Result<(), VyperErrors> {
347        let compiler_output = Command::new(self.get_vyper())
348            .arg("-f")
349            .arg("userdoc")
350            .arg(self.path_to_code.to_string_lossy().to_string())
351            .output()?;
352        if compiler_output.status.success() {
353            let mut buffer = BufWriter::new(File::create("./userdoc.txt")?);
354            buffer.write_all(&compiler_output.stdout)?;
355            Ok(())
356        } else {
357            Err(VyperErrors::CompilerError(
358                String::from_utf8_lossy(&compiler_output.stderr).to_string(),
359            ))?
360        }
361    }
362    /// Natspec dev documentation for vyper contract
363    pub fn devdoc(&self) -> Result<(), VyperErrors> {
364        let compiler_output = Command::new(self.get_vyper())
365            .arg("-f")
366            .arg("devdoc")
367            .arg(self.path_to_code.to_string_lossy().to_string())
368            .output()?;
369        if compiler_output.status.success() {
370            let mut buffer = BufWriter::new(File::create("./devdoc.txt")?);
371            buffer.write_all(&compiler_output.stdout)?;
372            Ok(())
373        } else {
374            Err(VyperErrors::CompilerError(
375                String::from_utf8_lossy(&compiler_output.stderr).to_string(),
376            ))?
377        }
378    }
379}
380
381/// Represents multiple vyper contract allocated on the stack, synchronous / blocking API for
382/// multiple compilations with scoped threads
383#[derive(Debug, Hash, Default, Eq, PartialEq, Ord, PartialOrd)]
384pub struct VyperStack<'a>(pub &'a mut [Vyper<'a>]);
385
386impl<'a> VyperStack<'a> {
387    pub fn compile_many(&mut self) -> Result<(), VyperErrors> {
388        thread::scope(|s| {
389            for i in self.0.iter_mut() {
390                s.spawn(|| -> Result<(), VyperErrors> {
391                    i.compile()?;
392                    Ok(())
393                });
394            }
395        });
396
397        Ok(())
398    }
399
400    pub fn compile_many_ver(&mut self, evm_version: &Evm) -> Result<(), VyperErrors> {
401        thread::scope(|s| {
402            for i in self.0.iter_mut() {
403                s.spawn(|| -> Result<(), VyperErrors> {
404                    i.compile_ver(evm_version)?;
405                    Ok(())
406                });
407            }
408        });
409
410        Ok(())
411    }
412
413    pub fn gen_abi_many(&self) -> Result<(), VyperErrors> {
414        thread::scope(|s| {
415            for i in self.0.iter() {
416                s.spawn(|| -> Result<(), VyperErrors> {
417                    i.gen_abi()?;
418                    Ok(())
419                });
420            }
421        });
422
423        Ok(())
424    }
425}
426
427/// Represents multiple vyper contracts
428#[derive(
429    Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default, Serialize, Deserialize,
430)]
431pub struct Vypers {
432    pub path_to_code: Vec<PathBuf>,
433    pub bytecode: Option<Vec<String>>,
434    pub abi: Vec<PathBuf>,
435    pub venv: Option<PathBuf>,
436}
437
438impl Vypers {
439    /// Constructor function that takes in the paths to your vyper contracts and the _desired paths/{names}.json for your ABIs
440    pub fn with_all(
441        paths: Vec<PathBuf>,
442        abi_paths: Vec<PathBuf>,
443        venv: Option<PathBuf>,
444    ) -> Self {
445        if paths.len() != abi_paths.len() {
446            panic!("Mismatched Vector Lengths");
447        }
448
449        Self {
450            path_to_code: paths,
451            bytecode: None,
452            abi: abi_paths,
453            venv,
454        }
455    }
456
457    pub fn new(paths: Vec<PathBuf>) -> Self {
458        let np = paths.iter().map(|e| e.with_extension("json")).collect();
459        Self {
460            path_to_code: paths,
461            bytecode: None,
462            abi: np,
463            venv: None,
464        }
465    }
466
467    pub fn in_dir(path: PathBuf) -> Option<Vypers> {
468        if let Ok(contracts) = get_contracts_in_dir(path) {
469            Some(Vypers::new(contracts))
470        } else {
471            None
472        }
473    }
474
475    pub async fn in_workspace(path: PathBuf) -> Option<Vypers> {
476        if let Ok(contracts) = utils::scan_workspace(path).await {
477            Some(Vypers::new(contracts))
478        } else {
479            None
480        }
481    }
482
483    pub fn with_venv(paths: Vec<PathBuf>, venv: &Path) -> Self {
484        let abis = paths.iter().map(|e| e.with_extension("json")).collect();
485
486        Self {
487            path_to_code: paths,
488            bytecode: None,
489            abi: abis,
490            venv: Some(venv.to_path_buf()),
491        }
492    }
493
494    pub fn set_venv(mut self, venv: PathBuf) -> Vypers {
495        self.venv = Some(venv);
496        self
497    }
498    pub fn get_vyper(&self) -> String {
499        if let Some(venv) = &self.venv {
500            if cfg!(target_os = "windows") {
501                format!("{}/scripts/vyper", venv.to_string_lossy())
502            } else {
503                format!("{}/bin/vyper", venv.to_string_lossy())
504            }
505        } else {
506            "vyper".to_owned()
507        }
508    }
509
510    pub fn get_pip(&self) -> String {
511        if let Some(venv) = &self.venv {
512            if cfg!(target_os = "windows") {
513                format!("{}/scripts/pip3", venv.to_string_lossy())
514            } else {
515                format!("{}/bin/pip3", venv.to_string_lossy())
516            }
517        } else {
518            "pip3".to_owned()
519        }
520    }
521
522    /// Compile multiple vyper contracts concurrently on new threads, updates the ABI field in Vypers
523    pub async fn compile_many(&mut self) -> Result<(), VyperErrors> {
524        let path = Arc::new(self.path_to_code.clone());
525        let mut out_vec: Vec<String> = Vec::with_capacity(self.path_to_code.len());
526        let mut threads: Vec<JoinHandle<Result<String, VyperErrors>>> = vec![];
527        let vy: Arc<String> = Arc::new(self.get_vyper());
528        for i in 0..self.path_to_code.len() {
529            let paths = Arc::clone(&path);
530            let bin = Arc::clone(&vy);
531            let cthread = tokio::spawn(async move {
532                let compiler_output =
533                    Command::new(bin.as_str()).arg(&paths[i]).output()?;
534                if compiler_output.status.success() {
535                    let mut out =
536                        String::from_utf8_lossy(&compiler_output.stdout).to_string();
537
538                    for _ in 0..1 {
539                        out.pop();
540                    }
541                    if !out.starts_with("0x") {
542                        if let Some(e) = out.split(":").last() {
543                            Ok(e.to_owned())
544                        } else {
545                            Err(VyperErrors::StringParsingError)
546                        }
547                    } else {
548                       Ok(out) 
549                    }
550                } else {
551                    Err(VyperErrors::CompilerError(
552                        String::from_utf8_lossy(&compiler_output.stderr).to_string(),
553                    ))?
554                }
555            });
556            threads.push(cthread);
557        }
558        for child_thread in threads {
559            let x = child_thread.await??;
560            out_vec.push(x);
561        }
562        self.bytecode = Some(out_vec);
563        Ok(())
564    }
565
566    /// Compile multiple vyper contracts concurrently on new threads, updates the ABI field in Vypers. `Ver` arg is for specifying EVM version to compile each contract to.
567    pub async fn compile_many_ver(&mut self, ver: Evm) -> Result<(), VyperErrors> {
568        let path = Arc::new(self.path_to_code.clone());
569        let vy = Arc::new(self.get_vyper());
570        let mut out_vec: Vec<String> = Vec::with_capacity(self.path_to_code.len());
571        let version = ver.to_string();
572        let mut threads: Vec<JoinHandle<Result<String, VyperErrors>>> = vec![];
573        for i in 0..self.path_to_code.len() {
574            let paths = Arc::clone(&path);
575            let bin = Arc::clone(&vy);
576            let cver = version.clone();
577            let cthread = tokio::spawn(async move {
578                let compiler_output = Command::new(bin.as_str())
579                    .arg(&paths[i])
580                    .arg("--evm-version")
581                    .arg(cver)
582                    .output()?;
583                if compiler_output.status.success() {
584                    let mut out =
585                        String::from_utf8_lossy(&compiler_output.stdout).to_string();
586                    for _ in 0..1 {
587                        out.pop();
588                    }
589                    if !out.starts_with("0x") {
590                        if let Some(e) = out.split(":").last() {
591                            Ok(e.to_owned())
592                        } else {
593                            Err(VyperErrors::StringParsingError)
594                        }
595                    } else {
596                       Ok(out) 
597                    }
598                } else {
599                    Err(VyperErrors::CompilerError(
600                        String::from_utf8_lossy(&compiler_output.stderr).to_string(),
601                    ))?
602                }
603            });
604            threads.push(cthread);
605        }
606        for child_thread in threads {
607            let x = child_thread.await??;
608            out_vec.push(x);
609        }
610        self.bytecode = Some(out_vec);
611        Ok(())
612    }
613
614    /// Generates ABIs for each vyper contract concurrently
615    pub async fn gen_abi_many(&mut self) -> Result<(), VyperErrors> {
616        let abi_path = Arc::new(self.abi.clone());
617        let vy = Arc::new(self.get_vyper());
618        let c_path = Arc::new(self.path_to_code.clone());
619        let mut threads: Vec<JoinHandle<Result<(), VyperErrors>>> = vec![];
620        for i in 0..c_path.len() {
621            let c = Arc::clone(&c_path);
622            let abi = Arc::clone(&abi_path);
623            let bin = Arc::clone(&vy);
624            let cthread = tokio::spawn(async move {
625                let compiler_output = Command::new(bin.as_str())
626                    .arg("-f")
627                    .arg("abi")
628                    .arg(&c[i])
629                    .output()?;
630                if compiler_output.status.success() {
631                    let json = serde_json::from_str::<Value>(&String::from_utf8_lossy(
632                        &compiler_output.stdout,
633                    ))?;
634                    let file = File::create(&abi[i])?;
635                    to_writer_pretty(file, &json)?;
636                } else {
637                    Err(VyperErrors::CompilerError(
638                        String::from_utf8_lossy(&compiler_output.stderr).to_string(),
639                    ))?
640                }
641                Ok(())
642            });
643            threads.push(cthread);
644        }
645        for child_thread in threads {
646            child_thread.await??
647        }
648        Ok(())
649    }
650
651    pub async fn get_abi_many(&self) -> Result<Vec<Value>, VyperErrors> {
652        let c_path = Arc::new(self.path_to_code.clone());
653        let mut threads: Vec<JoinHandle<Result<Value, VyperErrors>>> = vec![];
654        let vy = Arc::new(self.get_vyper());
655        for i in 0..self.path_to_code.len() {
656            let c = Arc::clone(&c_path);
657            let bin = Arc::clone(&vy);
658            let cthread = tokio::spawn(async move {
659                let compiler_output = Command::new(bin.as_str())
660                    .arg("-f")
661                    .arg("abi")
662                    .arg(&c[i])
663                    .output()?;
664                if compiler_output.status.success() {
665                    let json = serde_json::from_str::<Value>(&String::from_utf8_lossy(
666                        &compiler_output.stdout,
667                    ))?;
668                    Ok(json)
669                } else {
670                    Err(VyperErrors::CompilerError(
671                        String::from_utf8_lossy(&compiler_output.stderr).to_string(),
672                    ))?
673                }
674            });
675            threads.push(cthread);
676        }
677        let mut res_vec = Vec::new();
678        for child_thread in threads {
679            res_vec.push(child_thread.await??);
680        }
681        Ok(res_vec)
682    }
683}
684
685impl<'a> From<Vec<Vyper<'a>>> for Vypers {
686    fn from(value: Vec<Vyper>) -> Vypers {
687        let mut paths = vec![];
688        let mut abis = vec![];
689        let mut venv: Option<&Path> = None;
690
691        value.into_iter().for_each(|x| {
692            paths.push(x.path_to_code.to_path_buf());
693            abis.push(x.abi);
694            venv = x.venv;
695        });
696
697        match venv {
698            Some(v) => Vypers::with_venv(paths, v),
699            None => Vypers::new(paths),
700        }
701    }
702}
703
704#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
705pub enum Evm {
706    Byzantium,
707    Constantinople,
708    Petersberg,
709    Istanbul,
710    Berlin,
711    Paris,
712    Shanghai,
713    Cancun,
714    Atlantis,
715    Agharta,
716}
717
718impl Display for Evm {
719    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
720        match self {
721            Evm::Byzantium => write!(f, "{}", "byzantium".to_owned()),
722            Evm::Constantinople => write!(f, "{}", "constantinople".to_owned()),
723            Evm::Petersberg => write!(f, "{}", "petersberg".to_owned()),
724            Evm::Istanbul => write!(f, "{}", "istanbul".to_owned()),
725            Evm::Berlin => write!(f, "{}", "berlin".to_owned()),
726            Evm::Paris => write!(f, "{}", "paris".to_owned()),
727            Evm::Shanghai => write!(f, "{}", "shanghai".to_owned()),
728            Evm::Cancun => write!(f, "{}", "cancun".to_owned()),
729            Evm::Atlantis => write!(f, "{}", "atlantis".to_owned()),
730            Evm::Agharta => write!(f, "{}", "agharta".to_owned()),
731        }
732    }
733}