Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

fix(verify-bytecode): extract constructor arguments from creation code #8547

Merged
merged 1 commit into from
Jul 29, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading