Smart contracts have existed since the early 90s, envisioned by reputed cryptographer Nick Szabo. Despite the early roots, smart contracts became mainstream only after Ethereum’s introduction in 2015. And eight years later, these self-executing contracts are at the foundation of almost every other blockchain-specific activity.
Reading smart contracts is all about understanding the structure and code blocks. Some level of smart contract reading familiarity eventually paves the way to writing smart contracts using the Solidity programming language or any other platform-specific language. But the buck doesn’t stop at reading and writing smart contracts. As smart contracts are at the base of complex decentralized finance (DeFi) protocol and decentralized applications (DApps), they must be free of risks and vulnerabilities. Something that smart contract audits can help with. This guide covers everything relevant to reading, writing, and auditing smart contracts.
Learn more about smart contracts
Ethereum blockchain developer bootcamp with solidity (2023)
Ethereum and solidity: the complete developer’s guide
The complete solidity course: blockchain – zero to expert
- Reading smart contracts
- Smart contracts and their significance
- Characteristics of smart contracts
- Reading smart contracts based on the characteristics
- Reading other parts of a smart contract
- DApps and smart contracts: the relation
- Choosing the right blockchain platform
- Why should you learn to read smart contracts?
- How to write smart contracts
- Can anyone write a smart contract?
- Diving into programing
- Writing and deploying the first smart contract
- Smart contract development and best practices
- Interacting with the deployed smart contracts
- How to audit smart contracts?
- Strategies to audit smart contracts
- How to review the code right?
- Non-Ethereum chains and code review
- What are the different types of smart contract auditing?
- Best smart contract auditing practices
- Smart contract development and AI: the future
- Frequently asked questions
Reading smart contracts
Smart contracts are programmable bits of code that execute only when a set of conditions are met. They are synonymous with legally binding real-world contracts; only in this case, the code is the law. As smart contracts reside on the blockchain, they are immutable — they cannot be tampered with. It is this immutability quotient that makes smart contracts special, among other things.
Understanding smart contracts: basics and purpose
Smart contracts are meant to automate blockchain-specific transactions. As they are condition-specific contracts, they do not require intermediaries. What makes smart contracts useful is their compatibility across a wide range of use cases, including financial services, supply chain management, and more. And unlike traditional chunks of code that are programmed at a clip, smart contracts require highly secure and time-intensive strategies.
“The buzzword “web3” suggests the lax, security-poor programming habits of the web. When crypto or smart contracts are programmed like a web page, they are doomed. Sustainably successful blockchains and their apps are based on far more secure, careful, and slow programming methods.”
Nick Szabo, cryptographer and computer scientist: Twitter
Smart contracts can work with blockchain-specific tokens, say ERC-20 for the Ethereum blockchain, incentivizing efforts and moving transactions around. As code, conditions, and costs are involved, you should be careful about reading, writing, and auditing them.
Smart contracts and their significance
The real significance of smart contracts concerns their nature and positioning. For a given scenario — say a person A moving funds to person B when B completes a service — a copy of the smart contract is saved and executed by the blockchain nodes. Smart contracts are saved as contract codes within the chain. This multi-path validation is a blockchain-centric trait and keeps things secure.
Additionally, there exists sequential or synchronous smart contracts and asynchronous smart contracts where tasks are executed in parallel. Therefore, the type and purpose of a smart contract determines how it is written, read, or even audited.
Let us consider a standard smart contract-governed liquidity pool.
Imagine that the pool of tokens can be used for trading, and every time there is a successful trade happening, 0.3% of the total trade value is sent to the liquidity provider who made that trade possible or added liquidity for that given tradable asset. All the conditions highlighting the trade scenarios, trading fees, and conditions of non-compliance and trade failures are coded as a smart contract, which is stored within the chain as a contract code.
“SMART CONTRACTS automate transactions and allow business to happen anywhere, anytime, at minimal cost. This tech is being built into banking, finance, real estate, car sales, and more.”
Misha, web3 educator: X
Characteristics of smart contracts
We cannot dive deep into reading, writing, and auditing contracts if we aren’t aware of their characteristics. Here are the standard smart contract traits to be aware of:
Programmable contracts
Smart contracts are simply pieces of code. You can write smart contracts to execute commands and scenarios based on specific conditions. This is why smart contract developers and programmers are currently in demand, as most of the DeFi space already relies on smart contracts to process complex instances like handling trading fees across liquidity pools, maintaining APY ratios, and more.
Trustless
Smart contracts residing on the blockchain eliminate human intervention. This makes them entirely trustless. For instance, if a specific DeFi protocol, governed by smart contract(s), agrees to liquidate your assets once the value falls under a threshold, no human intervention can or should stop it. The code handles payment, performance, management, and rule enforcement, making the entire space completely trustless.
Autonomous
As mentioned earlier, smart contracts are loaded with self-executing instruction sets. In terms of coding, this means having iterations and loops built within the boilerplate. This ensures that tasks like payment, withdrawals, deposits, penalizing validators via slashing, and several other tasks are autonomously handled.
Secured
And finally, as smart contracts are secured using cryptography, breaching them is insanely difficult. Without a built-in vulnerability, bypassing a smart contract would mean trying to breach it in the open, in front of the entire blockchain.
Verifiable
Transactions processed via smart contracts are self-verifiable. This means that execution is proof enough that the transaction happened in the first place, as no human element is involved. The self-verifiable mechanism gives smart contracts an edge over traditional contracts governing legacy banking setups.
So the next time you plan on reading a smart contract, ensure that the boilerplate or the documentation has all the mentioned characteristics involved.
Reading smart contracts based on the characteristics
Here is a simple, smart contract representing an Escrow account. Users deposit their funds in the escrow, which then moves the same to the receiver after a particular timeframe.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Basic Smart Contract Boilerplate
contract SimpleTrustlessEscrow {
// State variables
address public depositor; // Account depositing ether
address payable public beneficiary; // Account receiving ether
uint256 public releaseTime; // Timestamp to release ether
// Events for verifying contract activity
event Deposited(address indexed _from, uint256 _value);
event Released(address indexed _to, uint256 _value);
// The contract constructor initializes the smart contract
constructor(address payable _beneficiary, uint256 _releaseTime) {
require(_releaseTime > block.timestamp, “Release time must be in the future”);
// Secure and Trustless: Contract binds depositor and beneficiary
depositor = msg.sender;
beneficiary = _beneficiary;
releaseTime = _releaseTime;
}
// Deposit function – autonomous execution (fallback function)
receive() external payable {
emit Deposited(msg.sender, msg.value);
}
// Release the ether to the beneficiary
function release() public {
// Programmable: Can only be executed after releaseTime
require(block.timestamp >= releaseTime, “Too early to release”);
// Autonomous: Automatically executes based on condition
uint256 amount = address(this).balance;
beneficiary.transfer(amount);
emit Released(beneficiary, amount);
}
}
While we will get to deciphering and reading this smart contract in detail, let us first check if the same adheres to the mentioned contract characteristics.
The “programmable” part
Look at the contract closely for this piece of code:
require(block.timestamp >= releaseTime, “Too early to release”);
uint256 amount = address(this).balance;
beneficiary.transfer(amount);
The funds are to be released only when a specific releaseTime condition is met, making these programmable contracts.
The “trustless” part
Here is a quick code snippet from the above:
depositor = msg.sender;
beneficiary = _beneficiary;
releaseTime = _releaseTime;
In the contract, everybody is code-bound from the depositor to the person receiving the funds. No one needs to interact with or trust the other as the function of transferring funds is bound by releaseTime — a code-based parameter.
The “autonomous” part
Here is the “fund release” part of the code:
function release() public {
require(block.timestamp >= releaseTime, “Too early to release”);
uint256 amount = address(this).balance;
beneficiary.transfer(amount);
emit Released(beneficiary, amount);
}
The entire process is autonomous, as funds are only released only when the releaseTime meets a certain criterion. Notice that the code isn’t partially programmable but fully autonomous.
Other elements of the smart contract code, including the deposit function, can also be made completely autonomous depending on the features you want to include. For instance, you can start a recurring deposit plan every time the user’s wallet crosses $100, with the excess amount moving to the beneficiary.
The “secured” part
Concerned as to which element lends security to the contract? Check out this part of the code:
constructor(address payable _beneficiary, uint256 _releaseTime) {
require(_releaseTime > block.timestamp, “Release time must be in the future”);
depositor = msg.sender;
beneficiary = _beneficiary;
releaseTime = _releaseTime;
}
Notice how there is a set precedence of the releaseTime function in relation to the timestamp. Nothing is random, and conditions must be met.
The “verifiable” part
Every transaction associated with the smart contract is logged within the chain, courtesy of separate log activity elements.
event Deposited(address indexed _from, uint256 _value);
event Released(address indexed _to, uint256 _value);
emit Deposited(msg.sender, msg.value);
emit Released(beneficiary, amount);
Reading other parts of a smart contract
Now that we have identified the elements that define the characteristics of a smart contract, here are the other contract elements to help you understand the drill better.
Pragma solidity ^0.8.0; – The version of the Solidity programming language needed to write this smart contract.
// SPDX-License-Identifier: MIT – Termed Software Package Data Exchange, this identifier states the license of the code release. It is advisable to include this to let people know if it’s open source and can be worked around or not.
Contract TimeLock { – Assigns name to the smart contract, like a label.
Address public depositor; – As the contract involves a depositor and a beneficiary, this is the point where public address of the depositor is mentioned. This variable is the Ethereum wallet address and is publicly viewable.
Address payable public beneficiary; – This is the public address of the beneficiary where the escrow transfers funds. It is also readable and lends a sense of transparency to blockchain-powered smart contracts.
Uint256 public releaseTime; – As it is a time-bound contract, the uint256 assigns the time-based variable to the contract. This will be the timeframe according to which the fund releases will happen.
In Solidity, uint (unsigned integer) is the way to assign integer-based values. The suffix 256 stands for large storage of numbers.
You can consider reading Solidity documentation to get acquainted with the syntax, expressions, and other code elements.
Other elements
constructor(address payable _beneficiary, uint256 _releaseTime) { – The “Constructor” is a one-time special function that gets called when the smart contract is deployed. It sets the contract in motion. Notice how at this point, all the address variables that we previously declared are called and initialized.
Receive() external payable { – This is a special function called when funds move to the contract address from outside. External suggests from outside, and “Payable” defines the nature of the move, that is, to receive ERC-20 tokens.
Function release() public { – This is a public function that states the movement of ERC-20 tokens from the contract address to the beneficiary. This function depends on releaseTime.
All these elements are parts of the hypothetical Escrow contract that we discussed. Ensure you go through the entire Solidity documentation to learn about the language better.
DApps and smart contracts: the relation
By now, you should have a headstart in reading and understanding an already-written smart contract. And many smart contracts like the ones we discussed make the backend of a decentralized application — a blockchain version of a standard mobile application.
Every characteristic of a smart contract, including contract security, autonomous and programmable execution, trustlessness of transactions, and more, is readily implemented while developing a decentralized application. So the next time you stumble upon a DApp, note that it is a smart contract-powered backend hosted on the blockchain — helping you initiate multiple tasks without human intervention. Smart contracts form the logic of DApps.
Blockchains for smart contracts
We know that Ethereum lets you develop smart contracts, like a massive software solution. However, it isn’t the only blockchain protocol around. If you want to dive deep into the world of smart contract development, you might want to look at other blockchains. Different blockchains have different parlances when it comes to chalking out contracts.
But first, let us discuss Ethereum — the go-to platform for most smart contract developers.
Ethereum
Smart contracts on Ethereum are written in the Solidity programming language. And the token interface for this smart contract development platform is ERC-20.
You can circle back to the Escrow-based smart contract that we discussed earlier to see how a standard Etheruem-based smart contract is written.
Even launching an ERC-20 token on the Ethereum blockchain is a smart contract-intensive feature, something we shall discuss in depth while writing a smart contract.
Here is what a basic code structure looks like, provided we plan to launch a new cryptocurrency BIC.
Consider this a hypothetical scenario. Not exactly launching a BIC cryptocurrency.
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract BICToken is ERC20 {
constructor(uint256 initialSupply) ERC20("BIC Token", "BIC") {
_mint(msg.sender, initialSupply);
}
}
We shall discuss every element of this code later when writing our smart contract.
Other blockchains
Like Ethereum, you can even create smart contracts on platforms like Solana, using Rust and Cardano, using Plutus, a subset of Haskell — a functional programming language.
Here is what a code structure in Rust (Solana) looks like:
Note: It is a simple contract where a counter gets incremented.
use anchor_lang::prelude::*;
declare_id!(“Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS”);
#[program]
pub mod hello_world {
use super::*;
pub fn initialize(ctx: Context<Initialize>) -> ProgramResult {
let greeting_account = &mut ctx.accounts.greeting_account;
greeting_account.counter = 0;
Ok(())
}
pub fn increment(ctx: Context<Increment>) -> ProgramResult {
let greeting_account = &mut ctx.accounts.greeting_account;
greeting_account.counter += 1;
Ok(())
}
}
Did you know? While Rust is the programming language for creating Solana-based smart contracts, Anchor is the smart contract development framework that is used. For creating smart contracts using Rust, developers need to pull modules from the Anchor framework — something the first line of our sample code (use anchor_lang::prelude::*;) stands for.
Solana documentation will help you understand the Rust-specific smart contract language.
Similarly, Cardano follows Plutus as the choice of language, followed by the Ink! language for Polkadot, TEAL for Algorand, C# for NEO, and more. Learning the chain-wise documentation in detail is advisable before proceeding with compatible smart contract writing.
Choosing the right blockchain platform
Still on the fence about choosing the right blockchain to write your smart contract? Follow this guide religiously:
Ethereum
Overview:
- Launch: 2015
- Consensus Mechanism: Proof of Stake (PoS) since the Ethereum 2.0 upgrade
- Smart Contract Language: Solidity
- Key Features:
- Turing-complete: Supports complex computations
- Largest developer community: Extensive resources and support
- Wide adoption: Many DeFi and NFT projects built on Ethereum
Advantages:
- Security: Highly secure due to its PoS consensus mechanism and large number of validators.
- Ecosystem: Rich ecosystem with numerous tools, libraries, and frameworks (e.g., Truffle, Hardhat, Remix).
- Liquidity and Interoperability: High liquidity for tokens and strong interoperability with other blockchains and dApps.
Disadvantages:
- Scalability Issues: Can suffer from network congestion, leading to high gas fees and slower transaction times.
- Cost: High transaction fees, especially during peak times.
Use Cases:
- Ideal for projects requiring high security and decentralization, such as DeFi platforms and complex dApps.
Binance Smart Chain (BSC)
Overview:
- Launch: 2020
- Consensus Mechanism: Proof of Staked Authority (PoSA)
- Smart Contract Language: Solidity (compatible with Ethereum)
- Key Features:
- High throughput: Capable of handling more transactions per second (TPS) than Ethereum.
- Low transaction fees: More cost-effective compared to Ethereum.
Advantages:
- Speed: Faster transaction times due to the PoSA consensus.
- Cost: Lower transaction fees, making it economical for users and developers.
- Compatibility: EVM compatibility allows easy migration of Ethereum dApps to BSC.
Disadvantages:
- Centralization Concerns: Fewer validators compared to Ethereum, leading to concerns about centralization.
- Security: While generally secure, it has faced high-profile hacks, raising some security concerns.
Use Cases:
- Suitable for applications requiring fast and inexpensive transactions, such as gaming, smaller DeFi projects, and NFTs.
Polkadot
Overview:
- Launch: 2020
- Consensus Mechanism: Nominated Proof of Stake (NPoS)
- Smart Contract Language: Substrate framework allows for custom blockchain development
- Key Features:
- Interoperability: Designed for connecting multiple blockchains.
- Scalability: Uses parachains to enhance scalability.
Advantages:
- Interoperability: Excellent for projects needing to interact with multiple blockchains.
- Customization: Allows developers to create customized blockchains tailored to specific needs.
- Scalability: Parachains and parallel processing enable high scalability.
Disadvantages:
- Complexity: More complex to develop and deploy compared to Ethereum and BSC.
- Early Stage: Still developing, with fewer established projects compared to Ethereum.
Use Cases:
- Ideal for projects needing high interoperability and scalability, such as cross-chain dApps and enterprises requiring bespoke blockchain solutions.
Criteria for Selecting the Appropriate Platform
- Project Requirements:
- Complexity: For highly complex smart contracts and dApps, Ethereum’s mature ecosystem is beneficial.
- Transaction Speed and Cost: If your project requires high throughput and low transaction fees, BSC is a strong candidate.
- Interoperability: For projects that need to operate across multiple blockchains, Polkadot offers significant advantages.
- Security:
- Decentralization: Ethereum’s high decentralization makes it more secure but can lead to higher costs.
- Validator Model: Assess the validator model of each platform to understand potential security risks.
- Developer Support:
- Tools and Resources: Ethereum has the most extensive resources, but BSC and Polkadot are catching up.
- Community: A strong developer community can provide support and accelerate development.
- Future Scalability:
- Long-Term Vision: Consider the platform’s roadmap and its plans for scalability and future upgrades.
- Ecosystem Growth: Platforms with growing ecosystems can offer better opportunities for collaboration and integration.
- Cost:
- Budget: Evaluate the cost of deploying and maintaining smart contracts on each platform. Ethereum might be costly during peak times, while BSC is generally more economical.
By carefully considering these factors, you can select the blockchain platform that best aligns with your project’s needs and goals
Why should you learn to read smart contracts?
The ability to write smart contracts is highly recognized, but even being able to read comes with its share of benefits:
- The ability to learn about the automation complexities associated with DeFi apps.
- Analyzing the ownership standards associated with asset tokenization.
- Understanding how decentralized autonomous organizations (DAOs) function.
- Understanding and implementing use-case-driven logic related to insurance, content monetization, voting system, royalties, and other verticals.
How to write smart contracts
Now that reading smart contracts is out of the way, let us focus on writing smart contracts. Before you delve deeper, it is necessary to reiterate that different blockchains might have different standards and languages related to smart contract development. It is necessary to focus on the standards defined by any given blockchain, To start with writing and contract deployment.
For the majority of our discussion, we shall focus on Ethereum as the chain and Solidity as the language.
The role of programming
Programming a smart contract is easily the most important part of the development cycle. And to get into smart contract development on Ethereum or any other blockchain, you should have some experience with non-blockchain programming languages like Javascript.
The ability to program a smart contract allows you to implement logic, handle the security elements of the same, optimize the code for gas fees, customize the same, and even make the same interoperable if needed.
EVM and smart contracts: a lay of the land
Anyone who is planning to write smart contracts on Ethereum needs to understand what the Ethereum Virtual Machine (EVM) is and how it works with smart contracts. For starters, EVM is an Ethereum component that gives programs an isolated and controlled environment to work. Consider this a global computer that hosts every piece of contract code there is on Ethereum. Every node on the Ethereum network runs the EVM.
If you are aspiring to be a smart contract developer, here is what you need to know in regard to smart contracts and EVM.
Once you write the program in Solidity, which is a high-level language, you need to compile it into bytecode — a machine-understandable low-level format. This bytecode gets into the Ethereum blockchain and resides there. Anyone interacting with the smart contract needs to send a transaction to the address of the contract.
Every node with the EVM installed can see this transaction, and once the validators approve the same, the smart contract code is executed. As every node has transaction visibility, nothing can be tampered with, and the code executes as it was written. And once the code gets executed, the blockchain’s state changes, making the process end-to-end and completely transparent.
Can anyone write a smart contract?
Writing smart contracts require technical know-how. But that’s not it. You also need to thoroughly understand how blockchain technology works, what language-specific needs are relevant to the blockchain you are targeting, interoperability, and more. In addition to that, you should also know a fair deal about smart contract vulnerabilities — things to avoid while writing code. And finally, contract testing and contract deployment knowledge is also a must.
All of that can become overwhelming. So here is a quick cheat sheet to get started:
- Start by choosing the platform or chain you want to work with.
- Learn the programming language associated with the chain, like Solidity for Ethereum.
- Learn about using the development tools like Integrated Development Environment like Remix.
- Start by writing your first contract and then testing using testnets.
- Once you are satisfied with the elements of the code, you can deploy it on-chain. Remember that deploying the contract on-chain will cost in the form of gas fees.
Here is a quick thread with some tips to write better smart contracts:
Diving into programing
It is time to get into to the technical aspects of smart contract development. Even though chains like Solana and Cardano allow you to develop smart contracts, Ethereum continues to be the most popular smart contract development platform.
Did you know? In 2022 alone, 100,000+ plus decentralized applications made it to the Ethereum network.
Why Ethereum?
Ethereum has a huge community of developers. Anything you develop will immediately get attention. Plus, its native language, Solidity, is relatively easy for individuals who know their way around Python or JavaScript. Finally, Ethereum global software, EVM, helps with seamless contract execution.
Solidity basics for your first contract
If you are in the majority and prefer to use Ethereum and Solidity, here is a quick list of things you need to track before getting started with smart contract development:
- Pragmas or the compiler version
- Contract definition for labeling
- State variables for storing data
- Events for EVM logging
- Modifiers to give specific rights to specific bodies
- Functions or the smart contract activities in play
- Inheritance for interoperability
- Understanding of control structures like if, else, for loops, data types like string, integer, and more.
Writing and deploying the first smart contract
Now that we know how things go down on-chain, let us dive into writing and deploying the first smart contract. Even though “Hello World” remains the first step, we will start by creating a smart contract to launch a hypothetical BIC token with a 100% unlocked supply of 1 million.
The basics
The first step is to install the latest version of Node.js and the NPM or Node Package Manager. This takes care of the development tools and the local environment for development. Also, Node.js and NPM allow you to set the web front-end for your smart contract.
Now, you need to set an IDE to write the contract code. For that, you can quickly install Visual Studio Code. Or you can cut the clutter and hop on to Alchemy — a blockchain development platform. With Alchemy, you can get some testnet ETH. This will cover the gas fees when you deploy the smart contract to the Goerli testnet or even the Sepolia testnet.
Do note that Sepolia is a younger testnet and therefore takes less disk space when it comes to node deployment.
For now, we will persist with the Goerli testnet as it has a larger number of deployed applications.
With the testnet and fake ETH ready, let us move to specifically writing the smart contract. Here is the code snippet for creating a BIC token with a 1 million fixed supply.
Note: We shall be deploying our smart contract locally on the MacOS and not the testnet. For testnet and mainnet deployment of smart contracts, we shall have a separate piece, which is beyond the scope of this discussion.
Steps to write and deploy
Here is the simple code snippet for the hypothetical token:
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract BICToken is ERC20 {
constructor() ERC20("BIC Token", "BIC") {
_mint(msg.sender, 1000000 * 10 ** decimals());
}
}
If you are aware of the syntax, you would know what each code component means. As for the Openzepplin part, it is the go-to library for importing ERC-20 smart contracts. This library offers the basic operating standards for ERC-20 tokens.
The mint function talks about the initial supply, which is deployed to the smart contract address or the msg.sender.
To set this code up locally and test it, we will need three components:
- Node.js and NPM (already discussed): Works like an engine to power your smart contracts
- Truffle: Works like a toolbox, helping you organize code, scripts, and other parts of a contract
- Ganache: Works like a virtual, on-device playground. Think of it like a personal blockchain.
What is contract execution?
If you have gone through the detailed process of writing a smart contract, it is imperative to know a fair bit about contract execution. It is a process by which the smart contract code is executed on a chain by the nodes.
It is the uniformity associated with contract execution that makes smart contracts transparent and immutable. Let us now understand the step-pronged process associated with contract execution:
Step 1
The code snippets that we have been writing need to be executed somewhere. In the case of smart contracts, this place of execution is the blockchain. The nodes or the participating members of the chain help execute the contract.
Step 2
The nodes accept the responsibility of executing contract code blocks in return for chain-related incentives. Every command or action that happens within the chain is led by smart contracts.
Step 3
Every smart contract has a specific address. For executing the contract, transactions are sent to that contract address. Do note that every node runs the EVM, which then has a copy of the smart contract code, making it easier to check for the authenticity of the transactions.
Step 4
The transactions targeted towards the smart contract are picked by the validators, who then include the same into specific blocks.
Step 5
Once the transaction is pushed through and successfully validated, it becomes a part of the blockchain. The smart contract function associated with the transaction is then called and executed across the blockchain nodes.
Step 6
Every node executing the smart contract should reach a deterministic conclusion — the same output for the same set of inputs — making the nature of the contracts completely trustless and transparent.
Note: Any error concerning the code execution or issues related to gas fees reverses the transactions. This means that the transaction based on a specific smart contract code will cease to exist. This is exactly what happens with flash loans when the inability to adhere to specific norms reverses the entire transaction, seemingly giving an impression that funds didn’t even move in the first place.
Every state change associated with smart contracts gets recorded within the blockchain and becomes an immutable part of the same.
Smart contract development and best practices
Now that you know a fair bit about smart contracts, here are a few pointers to get started with contract development:
- While writing contracts, relying on trusted libraries like the ones at OpenZeppelin to adhere to the desired code optimization and security standards is advisable.
- The idea is to keep the contract modular and simpler — making it easier to test and review.
- A good idea is to implement contract-specific access controls. This means declaring code bits where only a specific person or entity can change the crucial traits of the contract. Access control makes it easier to keep the smart contracts secure.
- While writing a contract, always make provisions for handling edge cases and exceptions — all while writing multiple tests.
Taking care of code sustainability
Each of the practices mentioned above helps with code optimization and security-specific implementations. However, there are a few contract-specific practices that you must follow and implement to take care of code sustainability. This aims to keep the contract code light and usable so that every node running and executing the same doesn’t have to dedicate a lot of computational power to the same.
- Handle storage efficiently by using smaller data sets. For instance, while writing a contract, use uint8 as the operational value instead of uint256.
- While writing a contract, it is advisable to optimize the code by combining multiple operations into one. We shall delve deeper into this in our detailed piece on “writing smart contracts.”
- A good idea is to use lazy evaluation in regard to smart contract execution. This way, you only need to execute a function when required and not every time something gets pushed to the smart contract address.
- Finally, relying on off-chain computations is also a good way to focus on sustainability. This helps lower gas fee requirements and can even speed up the contract execution.
Despite following the best practices while writing and developing smart contracts, it is necessary to focus on the contract security vulnerabilities while pushing them to the mainnet.
Interacting with the deployed smart contracts
Once a smart contract is deployed on the blockchain, interacting with it is crucial for utilizing its functionalities. Here’s how you can interact with deployed smart contracts using tools like MetaMask and blockchain explorers, along with examples of calling contract functions and reading contract data.
1. Using MetaMask
MetaMask is a popular browser extension and mobile app that serves as a cryptocurrency wallet and a bridge to the Ethereum blockchain. Here’s how to use it to interact with smart contracts:
a. Setting Up MetaMask:
- Install MetaMask: Add the MetaMask extension to your browser (available on Chrome, Firefox, Brave, and Edge) or download the mobile app.
- Create a Wallet: Follow the setup process to create a new wallet or import an existing one.
- Connect to the Network: Ensure MetaMask is connected to the correct Ethereum network (e.g., Mainnet, Ropsten, Kovan).
b. Interacting with Smart Contracts:
- Add Contract Address: Input the smart contract address in MetaMask to add it.
- Access Contract Functions: Use MetaMask to call functions within the contract. This usually requires interaction with a dApp interface that communicates with the contract.
Example:
- Connect to a dApp: Visit a decentralized application (dApp) that uses the deployed smart contract.
- Authorize MetaMask: Authorize the dApp to interact with your MetaMask wallet.
- Execute Functions: Perform actions such as sending transactions, executing smart contract functions, or retrieving data.
Calling a Function: To call a function like set
in a simple storage contract:
function set(uint x) public {
storedData = x;
}
- Initiate Transaction: Open the dApp, enter the desired value for
x
, and submit. - Confirm in MetaMask: MetaMask will prompt you to confirm the transaction and pay the gas fee.
Reading Data: For reading data from a contract:
function get() public view returns (uint) {
return storedData;
}
- Execute Read Operation: Click the relevant button in the dApp to call the
get
function. - No Gas Fee: Reading data does not require a gas fee.
2. Using Blockchain explorers
Blockchain explorers like Etherscan provide a user-friendly way to interact with smart contracts directly from your browser.
a. Accessing the Contract:
- Search for Contract: Enter the contract address in the search bar of a blockchain explorer like Etherscan.
- Contract Overview: View basic information about the contract, including its balance, transactions, and source code.
b. Interacting with the Contract:
- Read and Write Functions: Navigate to the “Contract” tab and then the “Read Contract” or “Write Contract” sections.
- Call Functions: Execute read functions to retrieve data or write functions to update the contract state.
Example:
- Read Function:
- View Data: In the “Read Contract” section, find the
get
function and click on it to retrieve the stored data.
- View Data: In the “Read Contract” section, find the
- Write Function:
- Invoke a Method: In the “Write Contract” section, connect your MetaMask wallet, input the parameters for the
set
function, and submit the transaction.
- Invoke a Method: In the “Write Contract” section, connect your MetaMask wallet, input the parameters for the
3. Example: Interacting with a simple storage contract
Smart Contract Code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SimpleStorage {
uint public data;
function set(uint x) public {
data = x;
}
function get() public view returns (uint) {
return data;
}
}
Deploying and Interacting:
- Deploy Contract: Use Remix to compile and deploy the
SimpleStorage
contract. - Use MetaMask: Connect MetaMask to Remix for deployment.
- Etherscan Interaction:
- Read Data: Use the
get
function on Etherscan to view the current value ofdata
. - Write Data: Use the
set
Function on Etherscan, input the desired value and confirm the transaction via MetaMask.
- Read Data: Use the
By utilizing tools like MetaMask and blockchain explorers, developers and users can efficiently interact with deployed smart contracts, enhancing the usability and functionality of decentralized applications.
How to audit smart contracts?
Every smart contract that has a presence in the mainnet needs to be evaluated for code performance, security, and other traits. This is where auditing — a rigorous contract testing process — comes to the fore, allowing you to uncover potential contract vulnerabilities and weaknesses.
Here is a quick audit checklist to get started:
Relation between reading, writing, and auditing: why even audit smart contracts?
While reading and writing smart contracts are intertwined when it comes to developing intelligent pieces of code, auditing has a special place and involves checking the logic in the first place. When it comes to blockchain-based code execution, everything is immutable, and anything catastrophic can have irreversible consequences upon contract execution. This is exactly why a thorough check of the contract code and other aspects via auditing is necessary.
Contract vulnerabilities and fixes
There can be a host of contract vulnerabilities that a detailed smart contract audit can identify. These include checking for reentrancy attacks, overflows or underflows, issues related to access control, and more. Once the exact nature of the issue is determined, the auditor can even suggest the best practices to fix the same.
Breaches cast studies and learnings
Still unsure as to how smart contract auditing can help? Well, let us circle back to the infamous DAO hack in 2016, which exploited a reentrancy issue and caused a loss of almost 3.6 million ETH. Similarly, there was the Parity wallet contract hack in 2017, leading to a loss of almost 500,000 ETH. These issues could have been avoided with the right set of audits.
Strategies to audit smart contracts
There are numerous strategies to audit smart contracts. Some of the more popular ones include:
Auditing tools
These tools act as the first set of defenses and are best used for locating common vulnerabilities. Some of the more popular tools include Securify, Mythril, and more — capable of performing static analysis of the code, detecting breach patterns, and helping get a security-specific headstart.
Code review
This is where manual code reviewers come into the picture, scrutinizing the codebase and identifying complex vulnerabilities, if any. A manual review can help take care of the business logic, context, and usage patterns.
Here is how manual code reviews help you locate threats:
Automatic scans
Tools like Snyk and GuardRails help with automatic contract scanning — a security implementation that gets invoked every time the code is updated. This form of audit ensures that new changes made to a code are safe and non-invasive in nature.
Formal verification
This is a complex process that solely relies on checking the business logic of the code. Do note that formal verification isn’t actually meant for verifying the syntax but only the logic to see if the code executes as desired.
In addition to the mentioned strategies, smart contract auditing can also be initiated using peer reviews, bug bounty programs, and test coverages via tools like the Solidity Coverage for maximizing efficacy.
How to review the code right?
If you are new to smart contract auditing, it is important to note that there are two ways of broadly analyzing the code and identifying issues. These include:
Static analysis
This type of code analysis helps identify basic security vulnerabilities, coding errors, and other issues per the given coding standards and conventions. Threats like unchecked calls to external sources, integer overflows, and more can be highlighted using static analysis. The best thing about static analysis is that the code doesn’t need to be executed for it to be checked.
Dynamic analysis
This approach towards auditing tests the alignment of the code with the EVM. Instead of only checking the code, dynamic analysis cross-checks the response of smart contracts to a wide range of inputs. Dynamic analysis can identify issues like incoherent gas consumption and even erroneous contract logic. Personal blockchain environments like Ganache can work as dynamic analysis platforms, allowing developers to make transactions, execute commands, and do much more with their contracts.
Testing an actual piece of code
Here is a smart contract snippet that works as a fund storage, having a withdrawal function:
pragma solidity ^0.6.1;
contract VulnerableContract {
mapping(address => uint256) public balances;
function deposit() public payable {
balances[msg.sender] += msg.value;
}
function withdraw(uint256 _amount) public {
require(balances[msg.sender] >= _amount, "Insufficient balance.");
(bool success, ) = msg.sender.call{value: _amount}("");
require(success, "Transfer failed.");
balances[msg.sender] -= _amount;
}
}
If you look at the code closely, there is a key vulnerability:
In the previous case, the “withdraw” function can be called again if the user receiving the funds is also a smart contract, albeit malicious. Therefore, before the last function or the balance update happens, a reentrancy attack can be initiated to transfer additional funds. Experienced auditors identify this kind of vulnerability.
Here is the fixed code for the same:
function withdraw(uint256 _amount) public {
require(balances[msg.sender] >= _amount, "Insufficient balance.");
balances[msg.sender] -= _amount;
(bool success, ) = msg.sender.call{value: _amount}("");
require(success, “Transfer failed.”);
}
Check how the balance update function gets called first and then the first move to the user. This change in operation order is what fixes the contract.
Non-Ethereum chains and code review
The world of decentralized applications and smart contracts has moved beyond Ethereum. Even though the bulk of action still happens within the Ethereum ecosystem, there are other chains like Cardano, Solana, and more that support smart contracts and require different auditing standards.
Why is every platform unique?
Different blockchains use different programming languages. The code’s semantics, syntax, and properties are different, making the smart contracts responsive to different writing and auditing practices. For instance, Ethereum uses Solidity, whereas Polkadot uses Ink and Rust — making it reactive to specific auditing standards.
Here is a quick tweet that doubles down as a Solidity tutorial:
Tools for going non-Ethereum
Now if you want to move beyond Ethereum, there are a few specialized auditing tools to get you started. For instance, with Cardano, there is the Marlowe suite for formal verification and auditing. When it comes to Solana, Rust-specific libfuzzer and cargo-fuzz are meant for auditing and contract testing. A multi-chain auditor must be familiar with these concepts to keep contract vulnerabilities at bay.
What are the different types of smart contract auditing?
Just to reiterate, you can segregate smart contract auditing into three types: manual, automatic, and hybrid. Do note that people prefer hybrid auditing strategies for complex contracts with deep business logic as they are the most comprehensive.
Outsourcing your audits
Organizations and individuals with minimal coding knowledge often outsource their writing and auditing requirements to reputed firms. When it comes to auditing, choosing the right company becomes all the more important as even though AI tools like ChatGPT can help write smart contract code, checking the same requires manual insights.
Also, here are the factors to take note of while outsourcing the auditing tasks:
Choosing the right firm
Before you zero in on the right outsourcing firm, it is crucial to check past audits, evaluate the experience, and even focus on the key team members.
Understanding responsibilities, costs, and overheads
Before making a hire, take note of the costs and services associated with the audits. It is imperative to first understand the nature of services offered — like issue identification, issue resolution, and more. You must also check if re-audits are also provided post implementing the first line of fixes. The cost of a smart contract audit can vary depending on the services, and therefore, it is necessary to track every requirement and offering before proceeding.
Best smart contract auditing practices
In case you want to ditch a firm and audit smart contracts yourself, here are the best strategies and practices to keep in mind:
- Always focus on a thorough code review, including the syntax and logic
- Start by picking up common vulnerabilities using tools like Slither, MythX, and more
- Surf through the Smart Contract Weakness Classification Registry or SWC to locate the known vulnerabilities and check for them beforehand.
- Test the smart contracts rigorously, including integration tests, unit tests, and more, to stress test the code across a wide range of scenarios.
- It is important to check for the chances of reentrancy attacks specifically. The best way to combat the same is to check for recursive calls that hackers can call before the first smart contract function.
- Focus on functions that lead to external calls. An error in this regard can alter the state and control flow, which isn’t desirable.
- Always keep an eye on code bits that point to gas usage. You do not want your contract to initiate interactions that are prohibitively expensive.
Smart contract development and AI: the future
Artificial intelligence is indeed making it easier to write smart contracts. However, regardless of AI innovation, the ability to audit smart contracts in the best possible way still requires human intervention. Therefore, if you plan to build your next web3 product emphasizing smart contracts and decentralized applications, it is crucial to focus religiously on the best auditing resources for your smart contracts. With cryptocurrency hacks and breaches surfacing with each passing day and hackers planning new strategies to break through, auditing a contract to perfection is certainly one of the more important modern-day skill sets.
Frequently asked questions
How is a smart contract written?
What language are smart contracts written in?
Are smart contracts written in solidity?
How are smart contracts audited?
How do you read data from a smart contract?
What type of audit is done in the initial stages of smart contract development?
How do you deploy a smart contract on the blockchain?
What are some common vulnerabilities in smart contracts?
Disclaimer
In line with the Trust Project guidelines, the educational content on this website is offered in good faith and for general information purposes only. BeInCrypto prioritizes providing high-quality information, taking the time to research and create informative content for readers. While partners may reward the company with commissions for placements in articles, these commissions do not influence the unbiased, honest, and helpful content creation process. Any action taken by the reader based on this information is strictly at their own risk. Please note that our Terms and Conditions, Privacy Policy, and Disclaimers have been updated.