Skip to content

Commit

Permalink
feat: support hardhat artifacts in vm.getCode (#956)
Browse files Browse the repository at this point in the history
Ports #903
  • Loading branch information
onbjerg committed Mar 22, 2022
1 parent 90a6aaf commit da67116
Showing 1 changed file with 31 additions and 2 deletions.
33 changes: 31 additions & 2 deletions forge/src/executor/inspector/cheatcodes/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use ethers::{
abi::{self, AbiEncode, Token},
prelude::{artifacts::CompactContractBytecode, ProjectPathsConfig},
};
use serde::Deserialize;
use std::{fs::File, io::Read, path::Path, process::Command};

fn ffi(args: &[String]) -> Result<Bytes, Bytes> {
Expand All @@ -18,6 +19,34 @@ fn ffi(args: &[String]) -> Result<Bytes, Bytes> {
Ok(abi::encode(&[Token::Bytes(decoded.to_vec())]).into())
}

/// An enum which unifies the deserialization of Hardhat-style artifacts with Forge-style artifacts
/// to get their bytecode.
#[derive(Deserialize)]
#[serde(untagged)]
#[allow(clippy::large_enum_variant)]
enum ArtifactBytecode {
Hardhat(HardhatArtifact),
Forge(CompactContractBytecode),
}

impl ArtifactBytecode {
fn into_inner(self) -> Option<ethers::types::Bytes> {
match self {
ArtifactBytecode::Hardhat(inner) => Some(inner.bytecode),
ArtifactBytecode::Forge(inner) => {
inner.bytecode.and_then(|bytecode| bytecode.object.into_bytes())
}
}
}
}

/// A thin wrapper around a Hardhat-style artifact that only extracts the bytecode.
#[derive(Deserialize)]
struct HardhatArtifact {
#[serde(deserialize_with = "ethers::solc::artifacts::deserialize_bytes")]
bytecode: ethers::types::Bytes,
}

fn get_code(path: &str) -> Result<Bytes, Bytes> {
let path = if path.ends_with(".json") {
Path::new(&path).to_path_buf()
Expand All @@ -37,10 +66,10 @@ fn get_code(path: &str) -> Result<Bytes, Bytes> {
.read_to_string(&mut buffer)
.map_err(|err| err.to_string().encode())?;

let artifact = serde_json::from_str::<CompactContractBytecode>(&buffer)
let bytecode = serde_json::from_str::<ArtifactBytecode>(&buffer)
.map_err(|err| err.to_string().encode())?;

if let Some(bin) = artifact.bytecode.and_then(|bytecode| bytecode.object.into_bytes()) {
if let Some(bin) = bytecode.into_inner() {
Ok(abi::encode(&[Token::Bytes(bin.to_vec())]).into())
} else {
Err("No bytecode for contract. Is it abstract or unlinked?".to_string().encode().into())
Expand Down

0 comments on commit da67116

Please sign in to comment.