1use 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#[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 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 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 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 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 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 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 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 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 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 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 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 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 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#[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#[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 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 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 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 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}