diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index a0b3a7c844c7..5834e961f179 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -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?; @@ -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() @@ -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();