Step-by-Step Guide: Verifying Ethereum Smart Contracts with solc and Etherscan.

Shrey Gajjar
4 min readNov 16, 2023

Overall, verifying smart contracts is a fundamental practice in the blockchain space to ensure security, transparency, and the responsible development of decentralized applications. Solc-JS is a set of JavaScript bindings for the Solidity compiler. The bytecode generation process using solc and its alignment with the bytecode observed on live networks, such as Etherscan, may exhibit variations. It's important to note that the bytecode produced by solc might not perfectly match the bytecode deployed on live networks. This discrepancy can arise due to several factors, including Solidity compiler versions, compiler settings, and the inclusion of external libraries. There are two types of verification process Partial Verification and Full Verification

Using a Legacy Version of Solidity with Solc-JS

The choice of Solidity Compiler Version can significantly impact the resulting bytecode. Select the stable version of solidity compiler rather than nightly. The solc compiler allows you to load specific versions using the loadRemoteVersion function, like this:

const solc = require('solc');
var solc_version = "v0.8.22+commit.4fc1097e"

var input = {
language: 'Solidity',
sources: {
'C1.sol': {
content: "contract C1{ function f() public pure {} }"
}
},
settings: {
outputSelection: {
'*': {
'*': ['*']
}
}
}
};

solc.loadRemoteVersion(solc_version, function (err, solc_specific) {
if(!err) {
const output = solc_specific.compile(JSON.stringify(input));
const finaloutput = JSON.parse(output);
var deployedBytecode = finaloutput.contracts['C1.sol']["C1"].evm.deployedBytecode.object;
}
}

It will generate the bytecode for C1 smartcontract. The output (object) above also contain other information such as metadata, abi, solidity-version, deployedBytecode, Bytecode, etc.

Multiple Contreac

Getting the Bytecode of an Existing Ethereum Contract

Having learned how to generate bytecode from a smart contract’s source code, the next step involves retrieving the existing bytecode from the blockchain for comparison. In Web3.js, this task is facilitated by the getCode() function, which retrieves the bytecode of a contract at a specified address. The function is implemented as follows:

var web3 = new Web3(new Web3.providers.HttpProvider("httpProvider"));

function getBlockchainByteCode (web3,contract_address) {
return (new Promise((resolve, reject) => {
resolve(web3.eth.getCode(contract_address).then(output =>
{
console.log("Blockchain bytecode : ",output)
return output;
}));

}))
}

getBlockchainByteCode(web3,"contract Address");

Contract Metadata

Few bytes of the bytecode generated will not match with the deployed bytecode that’s because of metadata.

This metadata encapsulates essential details like Solidity version and ABI. By default, the compiler appends the IPFS hash of the metadata file to the end of the runtime bytecode, not the creation bytecode. This approach allows for authenticated retrieval of the file without reliance on a centralized data provider when published. Alternative options include utilizing the Swarm hash or opting not to append the metadata hash to the bytecode.

At the end of the bytecode, the Solidity compiler appends a hash of the metadata file that gets generated at compile time. Eg: a165627a7a72305820 which is translated from 0xa1 0x65 'b' 'z' 'z' 'r' '0' 0x58 0x20. This bytecode translates to the assembly code which initializes the “free memory pointer”:

PUSH1 0x60 PUSH1 0x40 MSTORE

Because we might support other ways to retrieve the metadata file in the future, this information is stored CBOR-encoded.

{
"ipfs": "<metadata hash>",
// If "bytecodeHash" was "bzzr1" in compiler settings not "ipfs" but "bzzr1"
"bzzr1": "<metadata hash>",
// Previous versions were using "bzzr0" instead of "bzzr1"
"bzzr0": "<metadata hash>",
// If any experimental features that affect code generation are used
"experimental": true,
"solc": "<compiler version>"
}

Generated Bytecode: 0x6080604052348015600e575f80fd5b50600436106026575f3560e01c806326121ff014602a575b5f80fd5b60306032565b005b56fea2646970667358221220bde8b5c7708b94a5c8c09a58b47f4eaf1973f8ea7e005691d33c38191cccb1cb64736f6c63430008160033

Blockchain bytecode containing CBOR bytes.

The last two bytes in the bytecode indicate the length of the CBOR encoded information. By looking at this length, the relevant part of the bytecode can be decoded with a CBOR decoder.

So ultimately we don’t want to directly compare the two resulting bytecodes, but the specific parts of the bytecode which represent the smart contract’s logic. This type of verification are called as Partial Verification.

Partial Verification

Partial verification in the context of smart contract verification typically refers to a process where certain aspects of a smart contract are validated or checked, rather than conducting a full and exhaustive verification.

In partial verification, Instead of verifying the entire source code of a smart contract, developers might focus on matching the locally generated bytecode (compiled from the source code) with the bytecode deployed on the blockchain. This can be a quicker method to ensure that the compiled code matches expectations without a detailed source code review.

In conclusion, this blog demonstrates the practicality of partial verification in the context of smart contracts. By focusing on specific aspects such as bytecode generation, developers can efficiently perform targeted checks on the integrity and security of their contracts. The ability to generate bytecode using tools like solc provides a valuable means of comparison with the deployed bytecode on platforms like Etherscan.

--

--

Shrey Gajjar

Passionate about blockchain technology, I bring expertise in crafting secure and innovative solutions and make different blockchain core and explore technology.