Skip to content

Commit

Permalink
fix verify-bytecode: extract constructor arguments from creation code
Browse files Browse the repository at this point in the history
  • Loading branch information
klkvr committed Jul 28, 2024
1 parent 6822860 commit 97b33ed
Showing 1 changed file with 58 additions and 51 deletions.
109 changes: 58 additions & 51 deletions crates/verify/src/bytecode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,44 @@ impl VerifyBytecodeArgs {
};
let etherscan = Client::new(chain, key)?;

// Get creation tx hash
let creation_data = etherscan.contract_creation_data(self.address).await?;

trace!(creation_tx_hash = ?creation_data.transaction_hash);
let mut transaction = provider
.get_transaction_by_hash(creation_data.transaction_hash)
.await
.or_else(|e| eyre::bail!("Couldn't fetch transaction from RPC: {:?}", e))?
.ok_or_else(|| {
eyre::eyre!("Transaction not found for hash {}", creation_data.transaction_hash)
})?;
let receipt = provider
.get_transaction_receipt(creation_data.transaction_hash)
.await
.or_else(|e| eyre::bail!("Couldn't fetch transaction receipt from RPC: {:?}", e))?;

let receipt = if let Some(receipt) = receipt {
receipt
} else {
eyre::bail!(
"Receipt not found for transaction hash {}",
creation_data.transaction_hash
);
};

// Extract creation code
let maybe_creation_code =
if receipt.to.is_none() && receipt.contract_address == Some(self.address) {
&transaction.input
} else if receipt.to == Some(DEFAULT_CREATE2_DEPLOYER) {
&transaction.input[32..]
} else {
eyre::bail!(
"Could not extract the creation code for contract at address {}",
self.address
);
};

// Get the constructor args using `source_code` endpoint
let source_code = etherscan.contract_source_code(self.address).await?;

Expand All @@ -168,6 +206,11 @@ impl VerifyBytecodeArgs {
self.build_project(&config)?
};

let local_bytecode = artifact
.bytecode
.and_then(|b| b.into_bytes())
.ok_or_eyre("Unlinked bytecode is not supported for verification")?;

// Get the constructor args from etherscan
let mut constructor_args = if let Some(args) = source_code.items.first() {
args.constructor_arguments.clone()
Expand Down Expand Up @@ -201,63 +244,27 @@ impl VerifyBytecodeArgs {
.or(self.encoded_constructor_args.to_owned().map(hex::decode).transpose()?);

if let Some(provided) = provided_constructor_args {
if provided != constructor_args && !self.json {
println!(
"{}",
format!("The provided constructor args do not match the constructor args from etherscan ({constructor_args}).")
.yellow()
.bold(),
);
}
constructor_args = provided.into();
}

// Get creation tx hash
let creation_data = etherscan.contract_creation_data(self.address).await?;

trace!(creation_tx_hash = ?creation_data.transaction_hash);
let mut transaction = provider
.get_transaction_by_hash(creation_data.transaction_hash)
.await
.or_else(|e| eyre::bail!("Couldn't fetch transaction from RPC: {:?}", e))?
.ok_or_else(|| {
eyre::eyre!("Transaction not found for hash {}", creation_data.transaction_hash)
})?;
let receipt = provider
.get_transaction_receipt(creation_data.transaction_hash)
.await
.or_else(|e| eyre::bail!("Couldn't fetch transaction receipt from RPC: {:?}", e))?;

let receipt = if let Some(receipt) = receipt {
receipt
} else {
eyre::bail!(
"Receipt not found for transaction hash {}",
creation_data.transaction_hash
);
};

// Extract creation code
let maybe_creation_code =
if receipt.to.is_none() && receipt.contract_address == Some(self.address) {
&transaction.input
} else if receipt.to == Some(DEFAULT_CREATE2_DEPLOYER) {
&transaction.input[32..]
} else {
eyre::bail!(
"Could not extract the creation code for contract at address {}",
self.address
);
};
// In some cases, Etherscan will return incorrect constructor arguments. If this
// happens, try extracting arguments ourselves.
if !maybe_creation_code.ends_with(&constructor_args) {
trace!("mismatch of constructor args with etherscan");
// If local bytecode is longer than on-chain one, this is probably not a match.
if maybe_creation_code.len() >= local_bytecode.len() {
constructor_args =
Bytes::copy_from_slice(&maybe_creation_code[local_bytecode.len()..]);
trace!(
"setting constructor args to latest {} bytes of bytecode",
constructor_args.len()
);
}
}
}

// If bytecode_hash is disabled then its always partial verification
let has_metadata = config.bytecode_hash == BytecodeHash::None;

let local_bytecode = artifact
.bytecode
.and_then(|b| b.into_bytes())
.ok_or_eyre("Unlinked bytecode is not supported for verification")?;

// Append constructor args to the local_bytecode
trace!(%constructor_args);
let mut local_bytecode_vec = local_bytecode.to_vec();
Expand Down

0 comments on commit 97b33ed

Please # to comment.