Smart contracts are designed to be "trustless", meaning users shouldn't have to trust third parties before interacting with the contract. As a requirement for safety, users & developers must be able to verify a smart contract's source code — assuring that the published contract code matches the code deployed on the Ethereum blockchain.
The traditional process of verifying smart contracts is difficult, and so developers typically opt for using a centralized service (like Etherscan), defeating the purpose of trustlessness. But did you know that smart contracts are auto-verifiable through the Solidity compiler (i.e. solc) & IPFS alone?
Interacting with deployed contracts using web3 libraries like ethers.js requires having a copy of the contract ABI somewhere in your code, and keeping it up to date is a manual and error prone process. Using the thirdweb SDKs, ABIs are automatically inferred from any deployed contracts. All you need is the contract address. No ABI copy pasting, or maintenance required.
So how does it work under the hood? The answer might surprise you!
No databases, no servers - all it takes is a powerful, yet rarely discussed feature of the Solidity compiler. Let's take a closer look!
Self-verifying smart contracts
The Solidity team had the incredible foresight and ingenuity to make any contract auto verifiable from its bytecode alone. No Etherscan verification needed. The idea is to make the solc compiler encode some information about the contracts in the bytecode itself during the compilation process. Enough information to be able to safely auto verify any contract in a decentralized way.
When you compile a contract using any of the popular frameworks like hardhat, forge, truffle, etc, it enables the
bytecodeHash: "ipfs" compiler flag by default. It's such a powerful feature, yet so few people know about it. Let's see how it works.
How solc encodes your contract’s metadata
The Solidity compiler, solc, produces artifacts at the end of a successful compilation. Amongst these artifacts is one called contract metadata. This a standardized JSON object, containing useful information about the contract like its name, ABI, source code, formatted code comments and compiler settings.
When solc produces the compiled bytecode for a contract, it appends a few extra bytes at the end, allowing anyone to retrieve the original contract metadata that was produced when compiling this bytecode. But how?
The genius trick is taking advantage of my favorite property of IPFS and other content addressable storage solutions: knowing the address of a file before it's uploaded.
Here’s what solc does by default when compiling any solidity contract:
- Computes the IPFS hash of the contract metadata JSON artifact
- Encodes the computed IPFS hash using CBOR encoding
- Appends the encoded bytes at the end of the regular compiled bytecode
Contract auto-verification by design
This means that from any deployed contract, you can:
- Get the contract bytecode using the standard RPC call
- Decode the data at the end of the bytecode using a CBOR decoder
- Extract the IPFS hash from the decoded data
- Download the original contract metadata which contains source code, ABI, etc.
Since IPFS hashes are immutable, the contract metadata encoded this way is guaranteed to be correct and unmodified. This means that all contracts are auto-verifiable by design and without needing any third party!
So where’s the catch? Why is everybody still using centralized, closed source services to verify their contracts manually? The reason is that the compiler only computes the IPFS hash and encodes it, it does not actually upload anything to IPFS! This is where the thirdweb CLI comes in.
Deploying auto-verifiable contracts with thirdweb deploy
If you didn’t know already, thirdweb’s CLI is the swiss knife of web3 development. One of my favorites commands is
npx thirdweb deploy - it takes advantage of the solc contract metadata standard and makes deploying and integrating solidity contracts simpler and safer.
Here’s what happens when you run
npx thirdweb deploy on your solidity projects:
- Detect your current project framework (hardhat, forge, truffle, etc)
- Compile your contracts using your own project settings
- After compilation, upload your contract metadata to IPFS, making sure it matches exactly the encoded IPFS hash in the compiled bytecode
- Open up a web browser to fill in constructor parameters and deploy the contract without needing to hardcode private keys
By following the solidity contract metadata standard, contracts deployed using the thirdweb CLI are not only automatically verifiable, but also usable in any web3 app without worrying about copy pasting ABIs around!
No ABI needed to interact with contracts
The thirdweb SDKs allow you to load any contract just by its address, and get convenient and type safe wrappers for your contracts.
const sdk = new ThirdwebSDK("goerli");
const contract = sdk.getContract("0x..."); // no ABI needed!
const nfts = await contract.erc721.getAll();
Under the hood, the thirdweb SDKs:
- extract the contract metadata hash from the contract bytecode
- download it from IPFS, extract the ABI and cache it
- generate a type safe API for the contract
This approach provides a much nicer dev experience, as well as the following advantages:
- No need to embed ABIs in the source code means faster frontend startup times
- Single source of truth means ABIs are always up to date
- Works with any contract, on any EVM compatible chain
Since the solc contract metadata is an industry wide standard, it works with a variety of tools that also follow this standard! For example, contracts deployed with the thirdweb CLI are auto-verified on Sourcify, a decentralized contract verification service. And of course, any contract verified on Sourcify can be used with the thirdweb SDK without needing its ABI.
Powerful standards like these is what makes the web3 ecosystem magical, and this one deserves more love from the community!