Unit5 SCSA1615 SmartContract Final
Unit5 SCSA1615 SmartContract Final
Frontend
The frontend (client) is the user interface (UI) of a dApp. As in web2 it is often built
using popular web technologies like HTML, CSS, and JavaScript. Frameworks such
1
as React, Angular, and Vue.js are commonly used to create responsive and dynamic
UIs.
The frontend has to somehow communicate with the backend. For that purpose a
connection to a node is required and communication is utilized by libraries. The most
popular Javascript libraries are Web3.js and Ethers.js the most popular Python library
is web3.py. While we see more and more Javascript libraries and packages, especially
supporting React, being published, the Python ecosystem is less developed (Hint: there
is an obvious gap you could fill 🙌). These libraries provide utilities for handling
frontend logic, including signing transactions, retrieving account information, managing
web3 wallets, and more.
dApps also require the frontend to connect to a blockchain node before sending requests
to the dApp’s backend, which consists of smart contracts deployed on chain. While
running blockchain nodes yourself might reduce your dependency on any provider, it is
resource intensive. Providers like Alchemy, QuickNode, Tatum, Ankr or Infura offer
suitable solutions.
Shoutout to our frens at Alchemy, especially Vitto, for building the npx package create-
web3-dapp, including everything needed to start building a dApp super fast.
🌐 Hosting
Hosting refers to the process of storing and serving an application’s files, allowing users
to access and interact with it over the internet.
In traditional, centralized hosting, one or more dedicated servers are responsible for
serving the application’s content. This approach typically relies on a single entity, a
hosting provider. The vast majority of dApps today are using centralized hosting.
That creates a potential single point of failure as well as potential for cencorship.
Decentralized application hosting diverges from the centralized model by distributing
the application’s files across a network of nodes, each contributing storage and
bandwidth resources.
Solutions like IPFS or Swarm could be used. If you want to start hosting your dApp
decentralized, definitely check out Fleek. If you feel like decentralized hosting is an
overkill, still check out Fleek (their team simply did a really cool job hehe).
🪪 Wallets
Wallets are used to manage users’ digital assets, authenticate users and sign transactions
on the blockchain. In the context of dApps, wallets store users’ private keys and facilitate
2
transactions with the underlying smart contracts. There are a lot of different opinions
about what kind of wallets offer reasonable user experience, have low barrier to entry
but most importantly offer high security for the users (unfortunately there is a way too
large number of bad actors trying to steal private keys and get access to wallets through
social engineering!)
2 different kind of wallets are on the one hand browser-built-in wallets vs browser
extensions and custodial vs non-custodial wallets.
Some web browsers, such as Brave or Opera, come with built-in wallets that allow users
to manage their digital assets and interact with dApps directly within the browser.
Microsoft just recently announced they’ll also build in a native wallet into
its Edge browser.
Browser extension wallets, such as MetaMask or Trust Wallet, are separate add-ons that
users can install in their preferred browser. These wallets are typically more versatile
and support a wider range of cryptocurrencies and blockchain networks. While built-in
wallets are often limited in the number of supported blockchains today, extensions are
more vulnerable to attacks like phishing.
Custodial wallets are managed by third-party service providers, such as exchanges or
wallet services, that maintain control over users’ private keys. Users access their wallets
through the service provider’s platform, relying on them for security and asset
management. Non-custodial wallets give users complete control over their private keys,
allowing them to manage their digital assets without relying on a third party. Usually the
wallets that can be used to interact with dApps are non-custodial, so users have to
manage the private keys themselves. There are various different approaches to non-
custodial wallets like Magic, Argent or Metamask.
🔌 Nodes
Nodes are individual servers that participate in the blockchain network by validating and
relaying transactions. To interact with a dApp, the client/frontend must connect to a node
within the blockchain network.
However users can also interact with the dApp directly through a node or through
another smart contract! This means your frontend is not the only way how users might
be interacting with your backend if your smart contracts are deployed on a public
blockchain.
3
Services like Alchemy and QuickNode provide access to remote nodes, allowing
developers to focus on building their dApps without having to maintain their own
infrastructure. For spinning up a dedicated node but still not managing
yourself Chainstack is a viable solution. Alternatively, developers can run their own
nodes using software like Geth (Ethereum) or Solana’s Validator.
Nodes support read and write operations. If your application only allows to read data
from the blockchain no transaction fees need to be paid. If your dApp supports write
operations transaction fees (= gas fees) need to be paid. Tatum for example not only
provides nodes, but also provides a solution for the dApp developer to pay for the user’s
gas fees, so users don’t have to pay the gas fee themselves (which subsequently ensures
that your user can fully interact with your dApp even if their digital assets might not be
sufficient to pay for gas.)
📜 Smart Contracts
Smart contracts are the backbone of any dApp, allowing to write to and read from a
blockchain. Written in programming languages like Solidity (Ethereum) or Rust (Solana),
smart contracts define the logic and rules governing the dApp’s operations.
In a typical smart contract, once deployed, its code cannot be changed, which can lead to
difficulties when attempting to fix bugs or add new features. Upgradable (proxy) contracts
approach this issue by separating the contract logic from the contract’s data storage. These
contracts consist of two main components (basically 2 or more smart contracts):
1. Proxy Contract: The proxy contract acts as a middleman, forwarding incoming
requests to the logic contract while maintaining the contract’s state. This contract
remains unchanged during the upgrade process.
2. Logic Contract: The logic contract contains the actual code and business logic of
the application. Developers can deploy new versions of the logic contract and
update the proxy contract to point to the new logic, allowing seamless upgrades
without affecting the contract’s data.
A dApp can not only utilize the smart contract developed inhouse but also connect with
smart contracts (protocols) deployed by others. For example the popular dApp 1inch (a
DEX aggregator) has developed smart contracts to interact with the smart contracts of
various decentralized exchanges like Uniswap and Sushiswap.
A smart contract deployed on one chain (e.g. Ethereum) can not simply interact with
another smart contract deployed on another chain (e.g. Polygon).
4
Supporting multiple chains will help to address a larger user audience and might bring
advanced functionalities like zero knowledge proofs or faster transaction throughput. The
only way for such cross-chain interactions is to implement bridges like Wormhole or
interoperability protocols like Cosmos into the dApp.
⚙️ Indexing Solutions
Indexing solutions play a vital role in the decentralized application (dApp) ecosystem
by making blockchain data more accessible and queryable. As blockchains grow in size
and complexity, retrieving specific data directly from the chain can be slow and
resource-intensive.
Indexing solutions address this challenge by creating structured, indexed databases that
enable faster and more efficient data retrieval. Many indexing solutions offer real-time
data synchronization, ensuring that dApps are instantly notified when relevant events
occur. This allows dApps to trigger webhooks or actions in response to on-chain events
quickly and accurately.
The most commonly known solution is The Graph, which is a decentralized indexing
protocol, to develop subgraphs, but also many of the node providers like QuickNode
offer indexing features today.
🗄 Data Storage
While the blockchain is suitable for storing transactional data, it is not ideal for large-
scale data storage due to scalability and cost concerns.
Decentralized storage solutions like IPFS or Filecoin are often used for storing dApp
data off-chain, providing a more efficient and cost-effective storage option.
These services employ encryption and sharding to ensure data privacy and integrity.
Also centralized data storage can be used when building a dApp but it won’t be fully
decentralized anymore (same as with the hosting discussed above)
🔮 Oracles
Smart contracts execute predefined logic based on the information available within the
blockchain network.
However, many use cases require data from external sources (e.g., weather, stock prices,
or sports scores) to function effectively.
Oracles fulfill this need by securely providing off-chain data to smart contracts. Popular
oracles are Chainlink and UMA.
5
5.2. Connecting to the Blockchain and Smart Contract
6
experience. It allows you to undertake more complex tasks on a blockchain than simple token
exchanges.
Smart contracts eliminate the need for lengthy verification and paperwork to be completed.
There is no need for a third party to conduct these processes as the blockchain can automatically
ensure the contract is honoured by all parties involved.
Smart Contract Use Cases
Smart contracts are mostly used in decentralized application (dApp) development. They can be
used to automate payments or validation of ownership on a blockchain. Other applications also
include supply chain management, where shipments that do not follow the schedule are
automatically flagged for review.
The versatility of smart contracts lends itself to applications across a broad range of industries
and requirements.
Comparison of Smart Contracts and Blockchain
Blockchain vs Smart Contract: Similarities and Differences
Similarities Differences
Wide Applications – Both blockchains and
Dependence – Blockchains are independent
smart contracts are highly versatile tools. networks, while smart contracts are built on
They can be applied to a range of industries top of a blockchain. A smart contract is thus
like finance, supply chains, music and dependent on a blockchain.
movies and even medicine.
on a decentralised basis and do not require a using a blockchain. All you need is a crypto
central authority. This makes them highly wallet and some tokens you wish to transfer.
efficient and eliminates middlemen from the Meanwhile, smart contracts are more
equation. advanced features that may require more
familiarity with the ecosystem.
7
Step 3: More complex operations, such as determining the value of a derivative financial
instrument, or automatically releasing an insurance payment, might be encoded using more
sophisticated logic.
Step 4: The developers then use a smart contract writing platform to create and test the logic.
After the application is written, it is sent to a separate team for security testing.
Step 5: An internal expert or a company that specializes in vetting smart contract security could
be used.
Step 6: The contract is then deployed on an existing blockchain or other distributed ledger
infrastructure once it has been authorized.
Step 7: The smart contract is configured to listen for event updates from an "oracle," which is
effectively a cryptographically secure streaming data source, once it has been deployed.
Step 8: Once it obtains the necessary combination of events from one or more oracles, the
smart contract executes.
8
What is Web 3.0?
According to the Web3 Foundation, Web3 will provide “a decentralized and fair internet
where users are in control of their data.
Currently, if a seller wants to conduct a transaction with a buyer, both the buyer and the
seller must register with a “shop” or “platform” such as Amazon or similar e-commerce
portals.
What this ‘platform’ currently does is authenticate that the buyer and seller are the true
parties to the transaction. Web3 is about to remove the “platform” role.
Ordinary proofs supported by blockchain technology are used to authenticate the
purchaser. The same goes for sellers.
Blockchain permanently records the time and place of a transaction.
Web3 enables peer-to-peer (seller-to-buyer) transactions by eliminating the role of
intermediaries. This concept can be extended to other transactions as well. Imagine a
social media application that shares photos with your followers. It could be a blockchain-
backed broadcast operation by a person, and not all participants need social media
accounts to do it.
The spirit of Web3 is a distributed autonomous organization (DAO). This means that all
business and administrative rules for all transactions are transparently available and
software is built according to these rules.
9
Artificial intelligence (AI) and Machine learning:
Web 3.0 will also use machine learning, a branch of artificial intelligence (AI) that uses data
and algorithms to mimic human learning and incrementally improve its accuracy. These
capabilities will allow computers to generate faster, more relevant results in areas as diverse
as drug development and new materials, as opposed to targeted advertising, which dominates
current efforts.
Major Advantages:
Open network:
Web 3.0 is an open network and all applications and programs are developed with open-
source software. The code for development, which is a virtual resource, is open to the
community and the development process is kept transparent.
No Middlemen:
Web 3 technology can also eliminate middlemen, allowing sellers and customers to interact
directly. Non-fungible tokens already enable a lot of this in primarily static digital art, but
this arrangement could easily be replicated in music, film, and other media.
User control of data:
Core Web 3.0 features such as decentralization and permissionless systems give users more
control over their data. This limits data extraction practices and helps curb the network effects
that have allowed tech giants to become near-monopoly through exploitative advertising and
marketing practices.
Decentralized Monetization:
With centralized content management, user-generated content typically belongs to the
platform it was published on, but Web 3.0 can empower creators by giving them better
monetization opportunities. About 2 million professional content her creators in India can
benefit from.
Challenges/Concerns:
Massive Internet architecture needs to be overhauled:
From a technical perspective, Web3 requires a departure from the current architecture where
there is a front end, middle tier, and back end. Web3.0’s architecture requires backend
solutions for blockchain processing, storing and indexing data on the blockchain, peer-to-
peer communication, and more. Similarly, the middle tier, also called the business rules tier,
should contain blockchain-based backend processing.
10
Regulatory challenges:
It is argued that decentralization could lead to new types of cybercrime. Cybercrime, hate
speech, and misinformation are already difficult to monitor, and the lack of centralized
control makes them even more difficult in a decentralized structure. Cryptocurrency-based
crime remains an important issue to be addressed, given the increasing total transaction
volume that makes fraudulent transactions more valuable.
Lack of grievance procedures:
Its decentralized nature raises the question of who to contact in the event of a complaint and
who is responsible for data breaches.
Lack of content moderation:
Web 3.0 is silent about censorship. May produce obscene and provocative material. This
network makes it difficult to remove obscene or defamatory information, photos, or videos.
Way Ahead:
India has used technology to shape the socioeconomic development of the country.
(Examples include Bashar, Jan Dhan, UPI, COWIN, etc.). At the same time, India can
benefit from leading and catalyzing this early development of Web 3.0.
Web 3.0 can accelerate the value of India’s digital economy. In the face of such
opportunities, the startup ecosystem needs to be encouraged and revitalized to place India
well on the Web 3.0 map. Web 3.0 not only improves the user experience with digital
government services but also enables high-quality data for evidence-based policymaking.
From a government perspective, blockchain technology can be used to set up cross-
sectoral services faster.
The nature of Web 3.0 can be leveraged in academic and research fields to remove barriers
to patenting and utilizing available resources for global gain.
For example, blockchain technology is being used to store and sort vast amounts of data
related to viral DNA genome sequencing.
Countries and industry bodies must act now to establish open, ethical, and interoperable
systems with sound standards.
Definition of Web3.js
The individual description of web3 and JS serves as an important highlight for understanding
the JavaScript web3 connection. However, you must also understand the web3.js library, which
you can use for different functions.
First of all, you need to note the distinct highlights for developing
blockchain applications through the Ethereum blockchain. The important functions in
11
developing Ethereum-based blockchain applications include smart
contract development and website development. Smart contract development is
essential for crafting code with a Solidity programming language, which you would
deploy on the blockchain.
On the other hand, you must also develop websites that can interact with the blockchain.
The websites or clients would involve code that could read and write data from the
blockchain through smart contracts.
Web3.js can help you address the second important function in developing Ethereum-
based blockchain applications. Web3.js is practically a collection of libraries that can
help in performing different actions on Ethereum applications. Examples of actions
could include sending Ether between accounts, creating smart contracts and reading
and writing data from smart contracts.
Working of Web3.js
The obvious thing on your mind right now must be pointed at the Web3 JS Github entries
for some practical knowledge. However, you need to learn how web3.js works before
you start working on dApps or smart contracts.
Any web development expert would choose jQuery for making Ajax calls to a web server.
However, you can choose web3.js as an alternative for reading and writing to the
Ethereum blockchain.
Working of web3.js in enabling communication between clients and the Ethereum
blockchain. Web3.js communicates with the Ethereum blockchain by using the JSON-
RPC method or a Remote Procedure Call protocol. Ethereum is a decentralized peer-to-
peer network of nodes storing a copy of all the data and smart contract code on the
blockchain itself.
Now, web3.js could help in making requests to individual Ethereum nodes by leveraging
JSON-RPC for reading and writing data to the blockchain network. You can think of it
as implementing jQuery in JSON API for reading and writing data on a web server.
Important Dependencies in Web3.js
The introduction to web3.js would also draw attention to the dependencies used in the
JavaScript library for web3 development. Dependencies in web3.js are your ideal starting point
for web3 development with the assurance of distinct functionalities. The web3.js tutorial would
highlight the following important dependencies.
Node Package manager
12
The Node Package Manager or NPM is one of the first dependencies you would need for using
web3.js. It is available as a package with Node.js and can help you check whether the node has
been installed by visiting the terminal and typing the following command,
$ node –v
13
Eth: It is an interface for interacting with the Ethereum network. It provides functions for
querying the network and sending transactions.
Contract: It provides an interface for interacting with smart contracts. It provides the
functionality for interacting, deploying, and listening to events from deployed smart
contracts.
Smart Contract:
It is a self-executing program that runs on Ethereum Blockchain and automates the actions
required in an agreement or contract. All the transactions done through smart contracts are
irreversible and can be traced. Any transaction that modifies blockchain costs gas.
functions :
A function in Solidity is a piece of reusable code that can be called within or outside of a
smart contract. There is a keyword ‘function’ to define the function in solidity language,
which is followed by the function’s name and it is to be noticed that function’s name can’t
be any reserved keywords. Function can contains parameter or list of parameters denoting
the datatype and name of parameter. It’s optional to return or to not return any value from
function but it is defined at the time of declaration.
function function_name(parameter_list) scope returns(return_type) {
// block of code
}
Prerequisites:
how to write smart contracts in Solidity Language
contract deployment on the Testnet or Mainnet of Ethereum using Remix IDE
Approach:
Main Logic to call the function through web3.js is :
1. Install Web3.js – It is a JavaScript library that helps to interact with Ethereum-based
applications. One can install it, using a package manager like yarn or npm. For example,
using npm , run the following command in your terminal:
npm install web3
or
yarn add web3
14
2. Connection to a Provider – It is an object that allows us to communicate with nodes on
Ethereum Blockchain. You can use the default provider provided by MetaMask or make it
on your own like Infura. Here, we will use Metamask provider .
Here’s a command how to create a Web3 object and connect to the Metamask provider:
import Web3 from 'web3';
const web3 = new Web3(window.ethereum);
3. Contract ABI loaded from Remix – The ABI (Application Binary Interface) is a file that
describes the interface for the smart contract, including the functions that it has Copy the ABI
code from Remix IDE and paste it in file named as ‘ABI.json’. One can load the ABI by
fetching it from a URL or using the require function in Node.js. In this example, we’ll load
it using require:
import ABI from './ABI.json'
or
const ABI = require('./ABI.json');
4. Instance creation of the Contract – Web3 object is once instantiated and the contract
ABI, then one can create an instance of the contract using by calling the web3.eth.Contract()
function:
const contract = new web3.eth.Contract(
ABI,
"Paste the hash or address of smart contract here(example:
0x123456789abcdef123456789abcdef123456789')"
);
Note: Hash or address of smart contract is produced while deploying it on any blockchain.
Copy that hash and paste it above.
for testing purpose, deploy the smart contract on test network first and check its accuracy and
correctness and then after deploying it on main network.
5. Calling the Contract Functions –
Now, we can call any specific function of smart contract using contract.methods object.
Syntax :
contract.methods.<function_name>.call()
15
5.3.2.2. Interacting With Ethereum Smart Contract Using Web3js
Ethereum is a cryptocurrency platform in the market just like Bitcoin. It is an open-source &
decentralized blockchain featuring working on smart contracts. It has its own cryptocurrency
known as ether. The smart contracts in Ethereum are written in solidity.
TestRPC The Ethereum TestRPC is like a manual emulator for blockchain. It provides
blockchain interaction without running on an actual Ethereum node. It also provides all the
features required for testing of your blockchain. It is based on Node.js.
Web3.js Web3.js is an Ethereum-based JavaScript API that allows us to interact with a local
or remote Ethereum node using different servers such as HTTP. It interacts with the Ethereum
blockchain and can retrieve data from the blockchain as required by the developer.
Before we proceed to the steps, install TestRPC and Web3JS as follows:
1. TestRPC
Step 1: Open the console or command line and run the following commands to check if node
and npm are installed.
$ node -v
$ npm -v
If the above command goes unrecognized then download them from Nodejs.org for
windows or using the following commands (for macOS). The npm package is installed
along with the node.
$ brew install node
To update these packages use the following commands-
$ brew upgrade node
Step 2: Now install ethereumjs-testrpc using the following command-
install -g etherumjs-testrpc
Step 3: Now you can start the server using the following command
$ testrpc
2. Web3.js
Step 1: Open console or command line and move to the folder which contains the JavaScript
for your page as follows-
$ cd desktop/myproject
Step 2: Now, run the npm init command to create a package.json file, which will store project
dependencies:
$ npm init
16
Step 3: After completing the above installation run the following command for the
installation of web3.js-
$ npm install ethereum/web3.js0.20.0 –save
After the installation of TestRPC and Web3.js create a smart contract which you want to
connect with Web3.js.
Step 1: Open Remix-IDE.
Step 2: Create a sample smart contract as shown below or create any other smart contract.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Creating a contract
contract GFG{
// Defining a function to
// print a message
function getOutput() public pure returns (string memory)
{
return "Hi, your contract ran successfully";
}
}
Step 3: Compile your code and move to the Deploy section.
Step 4: Select the environment as Web3 Provider and enter the TestRPC
server http://localhost:8545, Make sure TestRPC is running on your system.
17
Step 5: Now using the following code, connect your webpage code to your smart contract.
Javascript
18
// Set the contrcat address
var contract_name = yourContract.at('PASTE CONTRACT ADDRESS HERE');
NOTE:
1. Variable names can be changed according to requirements.
2. You need to paste contract address and ABI as asked above in the code.
3. You will get the contract address when you deploy your smart contract.
4. You will get ABI when you compile your smart contract in compilation details.
5. You can use all your contract functions from here onwards.
6. The TestRPC command which we ran provides us 10 accounts, here we are using the
first one.
7. If you modify the code then you will have to redeploy it, and in that case you will have
to give the recent deployed address and the current ABI.
Output: The output can be customized as you want to display it on your webpage but in the
Remix-IDE the output will look like this after deploying the smart contract
Contract Output
19
5.3.2.3. Example 1: How To Make Active Navbar In HTML CSS And JavaScript?
An active navbar refers to a navigation bar on a website where the current or active page is
visually highlighted or distinguished from the other menu items. In this article, we are
going to learn how to make an Active Navbar in HTML, CSS, and JavaScript.
Prerequisites:
HTML
CSS
JavaScript
Approach
Use HTML to give structure using an unordered list (<ul>) and list items (<li>) for each
navigation element, assigning unique IDs or classes.
Design the navigation bar using CSS and give it a special look when a menu item is
selected.
In JavaScript, Select all navigation items with the class ‘.nav-item’
using const navItems = document.querySelectorAll('.nav-item');.
Iterate through each navigation item using navItems.forEach(item => {...}).
Add a click event listener to each navigation item to respond when a user clicks on it.
//script.js
document.addEventListener('DOMContentLoaded',
function () {
const navItems = document
.querySelectorAll('.nav-item');
navItems.forEach(item => {
item.addEventListener('click',
function () {
navItems.forEach(navItem => navItem
.classList.remove('active'));
this.classList.add('active');
});
});
});
20
Output:
5.3.2.4. Example 2: How to Create Shrink Header on Scroll using HTML, CSS and
JavaScript?
The Shrink Navigation bar works when the user scrolls down the page. In this article, we will
use HTML, CSS, and JavaScript to design a shrink navigation bar. HTML is used to create
the structure, and CSS is used to set the style of the HTML structure to make it looks good.
This kind of shrinking navbar looks attractive on a site. By using JavaScript you can easily
make the navigation bar shrinkable when the user scrolls down.
Creating Structure: In the HTML section, we will create a basic website structure for the
shrinkable navbar and when the user scrolls down the page it will display the effect.
Designing Structure: In CSS and JavaScript section, we will design the structure of the
navigation bar and then the scroll-down effect on the navbar using JavaScript.
html
CSS
Javascript
21
<!DOCTYPE html> <!-- Page Content -->
<html> <div class="content">
<head> <b>
<meta name="viewport" content A Computer Science Portal for
="width=device-width, initial- Geeks
scale=1"> </b>
<p>
<title> How many times were you frustrated
How to Create Shrink Header on Scroll while
using HTML, CSS and JavaScript ? looking out for a good collection of
</title> programming/algorithm/interview
</head> questions?
What did you expect and what did
<body> you get?
<!-- Navlist of header --> This portal has been created to
<div id="navlist"> provide
<a href="#default" id="logo"> well written, well thought and well
GeeksforGeeks explained solutions for selected
</a> questions.
<div id="navlist-right"> </p>
<a href="#home">Home</a> </div>
<a href="#about">Our Products</a> </body>
<a href="#about">Careers</a>
<a href="#contact">Contact US</a> </html>
<a href="#about">About US</a>
</div>
</div>
Output:
22
5.4. Voting Contract and App
The following contract is quite complex, but showcases a lot of Solidity’s features. It implements
a voting contract. Of course, the main problems of electronic voting is how to assign voting rights
to the correct persons and how to prevent manipulation. We will not solve all problems here, but
at least we will show how delegated voting can be done so that vote counting is automatic and
completely transparent at the same time.
The idea is to create one contract per ballot, providing a short name for each option. Then the
creator of the contract who serves as chairperson will give the right to vote to each address
individually.
The persons behind the addresses can then choose to either vote themselves or to delegate their
vote to a person they trust.
At the end of the voting time, winningProposal() will return the proposal with the largest number
of votes.
/ SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
/// @title Voting with delegation.
contract Ballot {
// This declares a new complex type which will
// be used for variables later.
// It will represent a single voter.
struct Voter {
uint weight; // weight is accumulated by delegation
bool voted; // if true, that person already voted
address delegate; // person delegated to
uint vote; // index of the voted proposal
}
23
// For each of the provided proposal names,
// create a new proposal object and add it
// to the end of the array.
for (uint i = 0; i < proposalNames.length; i++) {
// `Proposal({...})` creates a temporary
// Proposal object and `proposals.push(...)`
// appends it to the end of `proposals`.
proposals.push(Proposal({
name: proposalNames[i],
voteCount: 0
}));
}
}
24
// but in other situations, such loops might
// cause a contract to get "stuck" completely.
while (voters[to].delegate != address(0)) {
to = voters[to].delegate;
if (delegate_.voted) {
// If the delegate already voted,
// directly add to the number of votes
proposals[delegate_.vote].voteCount += sender.weight;
} else {
// If the delegate did not vote yet,
// add to her weight.
delegate_.weight += sender.weight;
}
}
25
}
}
}
5.5. Blind Auction Contract and Safe Remote Purchase Contract and App
26
// The triple-slash comments are so-called natspec
// comments. They will be shown when the user
// is asked to confirm a transaction or
// when an error is displayed.
if (highestBid != 0) {
// Sending back the Ether by simply using
// highestBidder.send(highestBid) is a security risk
// because it could execute an untrusted contract.
// It is always safer to let the recipients
// withdraw their Ether themselves.
pendingReturns[highestBidder] += highestBid;
}
highestBidder = msg.sender;
highestBid = msg.value;
emit HighestBidIncreased(msg.sender, msg.value);
}
27
if (amount > 0) {
// It is important to set this to zero because the recipient
// can call this function again as part of the receiving call
// before `send` returns.
pendingReturns[msg.sender] = 0;
// 1. Conditions
if (block.timestamp < auctionEndTime)
revert AuctionNotYetEnded();
if (ended)
revert AuctionEndAlreadyCalled();
// 2. Effects
ended = true;
emit AuctionEnded(highestBidder, highestBid);
// 3. Interaction
beneficiary.transfer(highestBid);
}
}
Blind Auction
The previous open auction is extended to a blind auction in the following. The advantage of
a blind auction is that there is no time pressure towards the end of the bidding period.
Creating a blind auction on a transparent computing platform might sound like a
contradiction, but cryptography comes to the rescue.
During the bidding period, a bidder does not actually send their bid, but only a hashed
version of it. Since it is currently considered practically impossible to find two (sufficiently
long) values whose hash values are equal, the bidder commits to the bid by that. After the
28
end of the bidding period, the bidders have to reveal their bids: They send their values
unencrypted, and the contract checks that the hash value is the same as the one provided
during the bidding period.
Another challenge is how to make the auction binding and blind at the same time: The only
way to prevent the bidder from just not sending the Ether after they won the auction is to
make them send it together with the bid. Since value transfers cannot be blinded in
Ethereum, anyone can see the value.
The following contract solves this problem by accepting any value that is larger than the highest
bid. Since this can of course only be checked during the reveal phase, some bids might be invalid,
and this is on purpose (it even provides an explicit flag to place invalid bids with high-
value transfers): Bidders can confuse competition by placing several high or low invalid
bids.
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.4;
contract BlindAuction {
struct Bid {
bytes32 blindedBid;
uint deposit;
}
29
modifier onlyAfter(uint time) {
if (block.timestamp <= time) revert TooEarly(time);
_;
}
constructor(
uint biddingTime,
uint revealTime,
address payable beneficiaryAddress
) {
beneficiary = beneficiaryAddress;
biddingEnd = block.timestamp + biddingTime;
revealEnd = biddingEnd + revealTime;
}
/// Reveal your blinded bids. You will get a refund for all
/// correctly blinded invalid bids and for all bids except for
/// the totally highest.
function reveal(
uint[] calldata values,
bool[] calldata fakes,
bytes32[] calldata secrets
)
external
onlyAfter(biddingEnd)
onlyBefore(revealEnd)
{
uint length = bids[msg.sender].length;
require(values.length == length);
require(fakes.length == length);
require(secrets.length == length);
uint refund;
for (uint i = 0; i < length; i++) {
Bid storage bidToCheck = bids[msg.sender][i];
(uint value, bool fake, bytes32 secret) =
(values[i], fakes[i], secrets[i]);
if (bidToCheck.blindedBid != keccak256(abi.encodePacked(value, fake,
secret))) {
// Bid was not actually revealed.
// Do not refund deposit.
continue;
}
refund += bidToCheck.deposit;
if (!fake && bidToCheck.deposit >= value) {
if (placeBid(msg.sender, value))
30
refund -= value;
}
// Make it impossible for the sender to re-claim
// the same deposit.
bidToCheck.blindedBid = bytes32(0);
}
payable(msg.sender).transfer(refund);
}
payable(msg.sender).transfer(amount);
}
}
31
There are multiple ways to solve this problem, but all fall short in one or the other way. In
the following example, both parties have to put twice the value of the item into the contract
as escrow. As soon as this happened, the Ether will stay locked inside the contract until the
buyer confirms that they received the item. After that, the buyer is returned the value (half of
their deposit) and the seller gets three times the value (their deposit plus the value). The idea
behind this is that both parties have an incentive to resolve the situation or otherwise their
Ether is locked forever.
This contract of course does not solve the problem, but gives an overview of how you can
use state machine-like constructs inside a contract.
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.4;
contract Purchase {
uint public value;
address payable public seller;
address payable public buyer;
modifier onlyBuyer() {
if (msg.sender != buyer)
revert OnlyBuyer();
_;
}
modifier onlySeller() {
if (msg.sender != seller)
revert OnlySeller();
_;
}
event Aborted();
event PurchaseConfirmed();
event ItemReceived();
event SellerRefunded();
32
// Ensure that `msg.value` is an even number.
// Division will truncate if it is an odd number.
// Check via multiplication that it wasn't an odd number.
constructor() payable {
seller = payable(msg.sender);
value = msg.value / 2;
if ((2 * value) != msg.value)
revert ValueNotEven();
}
buyer.transfer(value);
}
33
emit SellerRefunded();
// It is important to change the state first because
// otherwise, the contracts called using `send` below
// can call in again here.
state = State.Inactive;
seller.transfer(3 * value);
}
}
34
using web3.js and MetaMask, using the method described in EIP-712, as it provides a
number of other security benefits.
/// Hashing first makes things easier
var hash = web3.utils.sha3("message to sign");
web3.eth.personal.sign(hash, web3.eth.defaultAccount, function () {
console.log("Signed"); });
The web3.eth.personal.sign prepends the length of the message to the signed data. Since
we hash first, the message will always be exactly 32 bytes long, and thus this length
prefix is always the same.
What to Sign
For a contract that fulfills payments, the signed message must include:
A replay attack is when a signed message is reused to claim authorization for a second
action. To avoid replay attacks we use the same technique as in Ethereum transactions
themselves, a so-called nonce, which is the number of transactions sent by an account. The
smart contract checks if a nonce is used multiple times.
Another type of replay attack can occur when the owner deploys a ReceiverPays smart
contract, makes some payments, and then destroys the contract. Later, they decide to deploy
the RecipientPays smart contract again, but the new contract does not know the nonces
used in the previous deployment, so the attacker can use the old messages again.
Alice can protect against this attack by including the contract’s address in the message, and
only messages containing the contract’s address itself will be accepted. You can find an
example of this in the first two lines of the claimPayment() function of the full contract at
the end of this section.
Furthermore, instead of destroying the contract by calling selfdestruct, which is
currently deprecated, we will disable the contract’s functionalities by freezing it, resulting
in the reversion of any call after it being frozen.
35
Packing arguments
Now that we have identified what information to include in the signed message, we are ready
to put the message together, hash it, and sign it. For simplicity, we concatenate the data.
The ethereumjs-abi library provides a function called soliditySHA3 that mimics the behavior
of Solidity’s keccak256 function applied to arguments encoded using abi.encodePacked. Here
is a JavaScript function that creates the proper signature for the ReceiverPays example:
36
Writing a Simple Payment Channel
Alice now builds a simple but complete implementation of a payment channel. Payment
channels use cryptographic signatures to make repeated transfers of Ether securely,
instantaneously, and without transaction fees.
To open the payment channel, Alice deploys the smart contract, attaching the Ether to be
escrowed and specifying the intended recipient and a maximum duration for the channel to
exist. This is the function SimplePaymentChannel in the contract, at the end of this section.
37
Making Payments
Alice makes payments by sending signed messages to Bob. This step is performed entirely
outside of the Ethereum network. Messages are cryptographically signed by the sender and
then transmitted directly to the recipient.
Each message includes the following information:
The smart contract’s address, used to prevent cross-contract replay attacks.
The total amount of Ether that is owed to the recipient so far.
A payment channel is closed just once, at the end of a series of transfers. Because of this, only
one of the messages sent is redeemed. This is why each message specifies a cumulative total
amount of Ether owed, rather than the amount of the individual micropayment. The recipient
will naturally choose to redeem the most recent message because that is the one with the highest
total. The nonce per-message is not needed anymore, because the smart contract only honours
a single message. The address of the smart contract is still used to prevent a message intended
for one payment channel from being used for a different channel.
Here is the modified JavaScript code to cryptographically sign a message from the previous
section:
function constructPaymentMessage(contractAddress, amount) {
return abi.soliditySHA3(
["address", "uint256"],
[contractAddress, amount]
);
}
38
The smart contract must verify that the message contains a valid signature from the
sender. The process for doing this verification is the same as the process the recipient
uses. The Solidity functions isValidSignature and recoverSigner work just like their
JavaScript counterparts in the previous section, with the latter function borrowed from
the ReceiverPays contract.
Only the payment channel recipient can call the close function, who naturally passes the
most recent payment message because that message carries the highest total owed. If the
sender were allowed to call this function, they could provide a message with a lower
amount and cheat the recipient out of what they are owed.
The function verifies the signed message matches the given parameters. If everything
checks out, the recipient is sent their portion of the Ether, and the sender is sent the
remaining funds via a transfer. You can see the close function in the full contract.
Channel Expiration
Bob can close the payment channel at any time, but if they fail to do so, Alice needs a
way to recover her escrowed funds. An expiration time was set at the time of contract
deployment. Once that time is reached, Alice can call claimTimeout to recover her funds.
You can see the claimTimeout function in the full contract.
After this function is called, Bob can no longer receive any Ether, so it is important that
Bob closes the channel before the expiration is reached.
The full contract
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract Frozeable {
bool private _frozen = false;
modifier notFrozen() {
require(!_frozen, "Inactive Contract.");
_;
}
39
expiration = block.timestamp + duration;
}
/// the recipient can close the channel at any time by presenting a
/// signed amount from the sender. the recipient will be sent that amount,
/// and the remainder will go back to the sender
function close(uint256 amount, bytes memory signature)
external
notFrozen
{
require(msg.sender == recipient);
require(isValidSignature(amount, signature));
recipient.transfer(amount);
freeze();
sender.transfer(address(this).balance);
}
expiration = newExpiration;
}
/// if the timeout is reached without the recipient closing the channel,
/// then the Ether is released back to the sender.
function claimTimeout()
external
notFrozen
{
require(block.timestamp >= expiration);
freeze();
sender.transfer(address(this).balance);
}
/// All functions below this are just taken from the chapter
/// 'creating and verifying signatures' chapter.
function splitSignature(bytes memory sig)
internal
pure
returns (uint8 v, bytes32 r, bytes32 s)
{
require(sig.length == 65);
assembly {
// first 32 bytes, after the length prefix
r := mload(add(sig, 32))
// second 32 bytes
s := mload(add(sig, 64))
// final byte (first byte of the next 32 bytes)
v := byte(0, mload(add(sig, 96)))
40
}
return (v, r, s);
}
The function splitSignature does not use all security checks. A real implementation should
use a more rigorously tested library, such as openzepplin’s version of this code.
Verifying Payments
Unlike in the previous section, messages in a payment channel aren’t redeemed right away.
The recipient keeps track of the latest message and redeems it when it’s time to close the
payment channel. This means it’s critical that the recipient perform their own verification of
each message. Otherwise there is no guarantee that the recipient will be able to get paid in the
end.
The recipient should verify each message using the following process:
1. Verify that the contract address in the message matches the payment channel.
2. Verify that the new total is the expected amount.
3. Verify that the new total does not exceed the amount of Ether escrowed.
4. Verify that the signature is valid and comes from the payment channel sender.
We’ll use the ethereumjs-util library to write this verification. The final step can be done a
number of ways, and we use JavaScript. The following code borrows
the constructPaymentMessage function from the signing JavaScript code above:
// this mimics the prefixing behavior of the eth_sign JSON-RPC method.
function prefixed(hash) {
return ethereumjs.ABI.soliditySHA3(
["string", "bytes32"],
["\x19Ethereum Signed Message:\n32", hash]
);
}
41
function isValidSignature(contractAddress, amount, signature, expectedSigner) {
var message = prefixed(constructPaymentMessage(contractAddress, amount));
var signer = recoverSigner(message, signature);
return signer.toLowerCase() ==
ethereumjs.Util.stripHexPrefix(expectedSigner).toLowerCase();
}
Modular Contracts
A modular approach to building your contracts helps you reduce the complexity and
improve the readability which will help to identify bugs and vulnerabilities during
development and code review.
If you specify and control the behavior of each module in isolation, the interactions you
have to consider are only those between the module specifications and not every other
moving part of the contract.
In the example below, the contract uses the move method of the Balances library to check
that balances sent between addresses match what you expect. In this way,
the Balances library provides an isolated component that properly tracks balances of
accounts.
It is easy to verify that the Balances library never produces negative balances or
overflows and the sum of all balances is an invariant across the lifetime of the contract.
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.9.0;
library Balances {
function move(mapping(address => uint256) storage balances, address from, address to,
uint amount) internal {
require(balances[from] >= amount);
require(balances[to] + amount >= balances[to]);
balances[from] -= amount;
balances[to] += amount;
}
}
contract Token {
mapping(address => uint256) balances;
using Balances for *;
mapping(address => mapping(address => uint256)) allowed;
function transferFrom(address from, address to, uint amount) external returns (bool
success) {
require(allowed[from][msg.sender] >= amount);
allowed[from][msg.sender] -= amount;
42
balances.move(from, to, amount);
emit Transfer(from, to, amount);
return true;
}
Solidity Language
Solidity is a computer programming language used to create Ethereum smart
contracts. These contracts self-execute. The code and the agreements contained
therein are enforced by the blockchain network.
Solidity is a high-level language, meaning that it is designed to be human-readable
and easy to write. It is based on the JavaScript programming language and has a
syntax similar to C++.
Solidity is a statically-typed language, meaning that variables have a fixed type that
must be declared when they are created. It also supports complex data types such as
arrays and structs (collections of related variables).
One of the main benefits of Solidity is that it allows developers to build decentralized
applications (DApps) on the Ethereum platform.
These DApps can be used for a wide range of applications, including supply chain
management, voting systems, and financial applications.
Code Layout
Code layout refers to the way that your solidity code is structured and formatted. Proper
code layout helps to make your code more readable, maintainable, and easy to understand.
With respect to solidity code, here are some guidelines to maintain an ideal code layout
throughout your code.
1. Indentation
Using 4 spaces for per indentation instead of using a tab is considered a good practice.
Refer to the right and wrong snippets that are written ahead to understand better.
43
pragma solidity >=0.4.0 <0.9.0; pragma solidity >=0.4.0 <0.9.0;
// Right Practice for indentation // wrong practice for indentation
contract A { contract A {
// … // …
} }
contract B { contract B {
// … // …
} }
contract C { contract C {
// … // …
} }
2. Tabs or Spaces
Spaces are the preferred indentation method.
Mixing tabs and spaces should be avoided.
4. Curly Braces
Use curly braces to enclose the bodies of functions, control structures, and contracts. Place
the opening curly brace on the same line as the declaration. Place the closing curly brace on
a new line. Refer to the right and wrong snippets written ahead to understand better.
pragma solidity >=0.4.0 <0.9.0; pragma solidity >=0.4.0 <0.9.0;
// right practice // wrong practice
import “./Owned.sol”; import “./Owned.sol”;
contract A { contract A
// … {
} // …
}
46
7. Single Space Between Operators and Operands
Never add spaces while declaration involving special characters like (“[]”) etc. Add single
spaces between operators and operands. Refer to the right and wrong snippets written ahead
to understand better.
// right practice // wrong practice
unit[] a; unit [] a;
a = 7; a=7;
a = 100 / 10; a = 100/10;
a += 3 + 4; a += 3+4;
a |= c && d; a |= c&&d;
10. Imports
Imports should be sorted in alphabetical order. The same import should not be repeated. Use
the import “./filename”; syntax for external contract dependencies.
// Good // Bad
import "./filename1"; import "./filename1";
import "./filename2"; import "./filename1";
import "./filename3"; import "./filename3";
47
11. Order of Functions
Ordering helps readers identify which functions they can call to find the constructor and
fallback definitions easier.
Functions should be ordered in the following manner:
Constructor.
Constant functions.
Receive function (if exists)
Fallback function (if exists)
External
Public state-modifying functions.
Internal state-modifying functions.
Private state-modifying functions.
Within a grouping, place the view and pure functions last.
// Good
contract Example {
constructor() public { … }
function constantFunc1() public constant returns (…) { … }
function constantFunc2() public constant returns (…) { … }
function publicFunc1() public { … }
function publicFunc2() public { … }
function internalFunc1() internal { … }
function internalFunc2() internal { … }
function privateFunc1() private { … }
function privateFunc2() private { … }
}
// Bad
contract Example {
function internalFunc2() internal { … }
function privateFunc1() private { … }
constructor() public { … }
function constantFunc1() public constant returns (…) { … }
function publicFunc1() public { … }
function internalFunc1() internal { … }
48
function constantFunc2() public constant returns (…) { … }
function publicFunc2() public { … }
function privateFunc2() private { … }
}
12. Whitespace in Expressions
Use whitespace to increase readability and separate different elements in an
expression. Put spaces around binary and ternary operators (e.g. +, -, ? :).
// Good // Bad
uint x = (a + b) * c; uint x=(a+b)*c;
uint y = (a > b) ? a : b; uint y=(a>b)?a:b;
Avoid extraneous whitespace in the following situations:
1 Immediately inside parenthesis, brackets or braces, with the exception of single line
function declarations.
//Good : spam(ham[1], Coin({name: "ham"}));
//Bad : spam( ham[ 1 ], Coin( { name: "ham" } ) );
2 Exception: function singleLine() public { spam(); }
Immediately before a comma, semicolon:
//Good: function spam(uint i, Coin coin) public;
//Bad: function spam(uint i , Coin coin) public ;
3 More than one space around an assignment or other operator to align with another:
//Good //Bad
x = 1; x = 1;
y = 2; y = 2;
longVariable = 3; longVariable = 3;
4 Do not include a whitespace in the receive and fallback functions:
//Good
receive() external payable {
...
}
fallback() external {
...
}
//Bad
receive () external payable {
...
}
fallback () external {
...
}
49
13. Control Structures
The braces denoting the body of a contract, library, functions and structs should:
open on the same line as the declaration
close on their own line at the same indentation level as the beginning of the declaration.
The opening brace should be preceded by a single space.
Yes: No:
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.9.0; pragma solidity >=0.4.0 <0.9.0;
Yes: No:
if (...) { if (...)
... {
} ...
}
for (...) {
... while(...){
} }
for (...) {
...;}
For control structures whose body contains a single statement, omitting the braces is
ok if the statement is contained on a single line.
Yes: No:
if (x < 10) if (x < 10)
x += 1; someArray.push(Coin({
name: 'spam',
value: 42
}));
For if blocks which have an else or else if clause, the else should be placed on the
same line as the if’s closing brace. This is an exception compared to the rules of other
block-like structures.
Yes: No:
if (x < 3) { if (x < 3) {
x += 1; x += 1;
} else if (x > 7) { }
x -= 1; else {
} else { x -= 1;
x = 5; }
}
50
if (x < 3)
x += 1;
else
x -= 1;
// Good
function add(uint a, uint b) public returns (uint) {
return a + b;
}
// Bad
function add(uint a, uint b) {
return a + b;
}
function add(uint a, uint b) public returns (uint) {
return a + b;
}
The modifier order for a function should be:
Visibility
Mutability
Virtual
Override
Custom modifiers
51
Yes:
function balance(uint from) public view override returns (uint) {
return balanceOf[from];
}
function thisFunctionHasLotsOfArguments(address a,
address b,
address c,
address d,
address e,
address f) public {
doSomething();
}
function thisFunctionHasLotsOfArguments(
address a,
address b,
address c,
address d,
address e,
address f) public {
doSomething();
}
52
If a long function declaration has modifiers, then each modifier should be dropped to its own
line.
Yes:
function thisFunctionNameIsReallyLong(address x, address y, address z)
public
onlyOwner
priced
returns (address)
{
doSomething();
}
function thisFunctionNameIsReallyLong(
address x,
address y,
address z
)
public
onlyOwner
priced
returns (address)
{
doSomething();
}
No:
function thisFunctionNameIsReallyLong(address x, address y, address z)
public
onlyOwner
priced
returns (address) {
doSomething();
}
Multiline output parameters and return statements should follow the same style
recommended for wrapping long lines found in the Maximum Line Length section.
Yes: No:
function thisFunctionNameIsReallyLong( function thisFunctionNameIsReallyLong(
address a, address a,
address b, address b,
address c address c
) )
public public
returns ( returns (address someAddressName,
address someAddressName, uint256 LongArgument,
53
uint256 LongArgument, uint256 Argument)
uint256 Argument {
) doSomething()
{
doSomething() return (veryLongReturnArg1,
veryLongReturnArg1,
return ( veryLongReturnArg1);
veryLongReturnArg1, }
veryLongReturnArg2,
veryLongReturnArg3
);
}
contract C {
constructor(uint, uint) {
}
}
contract D {
constructor(uint) {
}
}
contract A is B, C, D {
uint x;
constructor(uint param1, uint param2, uint param3, uint param4, uint param5)
B(param1)
C(param2, param3)
D(param4)
{
// do something with param5
x = param5;
}
}
No:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
54
contract C {
constructor(uint, uint) {
}
}
contract D {
constructor(uint) {
}
}
contract A is B, C, D {
uint x;
constructor(uint param1, uint param2, uint param3, uint param4, uint param5)
B(param1)
C(param2, param3)
D(param4) {
x = param5;
}
}
contract X is B, C, D {
uint x;
constructor(uint param1, uint param2, uint param3, uint param4, uint param5)
B(param1)
C(param2, param3)
D(param4) {
x = param5;
}
}
55
16. Variable Declarations
Declare variables , followed by the variable name and it’s type. Declare variables as close
to their usage as possible, to increase readability. Initialize variables with a default value at
declaration if possible.
// Good // Bad
function example() public { function example() public {
uint a = 1; uint a;
uint b = 2; a = 1;
uint c = a + b; uint b = 2;
} uint c = a + b;
}
No:
str = 'bar';
str = '"Be yourself; everyone else is already taken." -Oscar Wilde';
56
Surround operators with a single space on either side.
// Good // Bad
x = 3; x=3;
x = 100 / 10; x = 100/10;
x += 3 + 4; x += 3+4;
x |= y && z; x |= y&&z;
Operators with a higher priority than others can exclude surrounding whitespace in
order to denote precedence. This is meant to allow for improved readability for
complex statements. You should always use the same amount of whitespace on either
side of an operator:
// Good // Bad
x = 2**3 + 5; x = 2** 3 + 5;
x = 2*y + 3*z; x = y+z;
x = (a+b) * (a-b); x +=1;
Order of layout
Order of layout refers to the recommended order in which different elements of a solidity
contract should be organized. It is important to follow a consistent order of layout in order to
make the code more readable and easier to understand. This order might vary slightly for
different programming languages. This order consists of several optional and mandatory
pointers. We’ve tried to cover almost all (both mandatory/optional) elements as per their
order of occurrences in the solidity code.
1. Pragma Statement
A pragma statement is a directive that specifies the version of Solidity that the contract is
compatible with. It is important to include a pragma statement at the beginning of your
contract to ensure that it will be compiled and run correctly on the Ethereum Virtual Machine
(EVM).
2. Import Statements
Import statements are used to include code from other files or libraries in your contract. While
using any predefined functionality, import statements are required. This can be useful for
including commonly used functions or for sharing code between contracts.
3. Contract and Library Definitions
A contract is a self-contained piece of code that can be deployed on the Ethereum blockchain.
It defines the functions and variables that make up the contract, as well as the logic for how
it will behave. Whereas a library is a special type of contract that cannot be deployed on its
57
own but can be used by other contracts. It is used to encapsulate common code that can be
shared between contracts.
4. Global Variables
Declare any global variables at the beginning of the contract. It is good practice to group
related variables together and to declare constants first, followed by variables that will be
modified during the execution of the contract.
5. Constructor
The constructor is a special function that is executed when the contract is deployed. It is used
to initialize the contract and set any initial values for variables. The constructor should be
placed immediately after the global variables.
6. Fallback Function
The fallback function is a special function that is executed whenever the contract receives an
external call that does not match any of the other functions. It is used to handle unexpected
or invalid calls to the contract. The fallback function should be placed immediately after the
constructor.
7. Variable Declarations
Variables are used to store data in a contract. In Solidity, variables must be declared with a
type, such as uint256 for unsigned integers or address for Ethereum addresses. Here these
variables are different from global variables, as they will only have scope in the contract they
are getting initialized or declared.
58
11. Events
Declare any events after the functions. Events are used to trigger log entries on the blockchain
that can be used for logging or for triggering external actions. An event is declared like a
function, but with the “event” keyword and no function body. It can have parameters, which
can be of any type except for mappings and dynamically sized arrays.
Naming Convention
A naming convention is a set of rules for choosing names for variables, functions, and other
important entities in your code. With respect to solidity code, here are some guidelines for
the naming convention while writing your code.
Use CamelCase for naming variables and functions (e.g. helloGeeks)
Use PascalCase for contract and struct names.
Use descriptive, lowercase names for variables and functions (e.g. totalSupply
instead of ts).
Use a leading underscore for private variables (e.g. _privateVariable).
Use a leading is or has for boolean variables (e.g. isActive or hasValue).
Use descriptive and meaningful names for constants, and use ALL_CAPS for
naming them.
Avoid abbreviations and single-letter names, except for common abbreviations
like “id” and “num”.
Avoid using reserved words as names.
Use descriptive and meaningful names for events, and prefix the name with Log.
Use descriptive and meaningful names for functions, and indicate their purpose in
the name.
Use descriptive and meaningful names for function arguments, and indicate the
purpose of the argument in the name.
Use meaningful names for local and state variables, and indicate their purpose in
the name
Use descriptive and meaningful names for modifiers, and indicate their purpose
in the name.
Use descriptive and meaningful names for enums, and indicate their purpose in
the name.
Refer to the code snippet attached ahead for reference.
contract MyContract {
// Constants
59
uint256 public constant MIN_VALUE = 10;
uint256 public constant MAX_VALUE = 100;
// Variables
uint256 public totalSum;
address public owner;
mapping(address => uint256) public balances;
…
NatSpec
NatSpec (Natural Language Specification) is a system for documenting solidity code in a
way that is easy for humans to understand. NatSpec comments are written in Markdown
format and can be accessed through the Ethereum NatSpec Standard JSON Interface. To add
single-line NatSpec documentation to your Solidity code, use double-slash comments (‘//’)
followed by the NatSpec tag and the documentation text. And for adding multiple comments
(“/**…..*/”) Here’s an example to refer to while writing comments.
/**
@notice Description of the function
@param Parameter1 Description of parameter1
@param Parameter2 Description of parameter2
@return Description of the return value
*/
Below is the solidity code to demonstrate how to comment using the NatSpec format:
Solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.6 <0.9.0;
/// @title A contract for demonstrate NatSpec format
/// @author Jitendra Kumar
/// @notice For now, this contract just show how to comment using NatSpec format
contract helloGeeks {
/// @notice Return an initialized string
/// @return A string helloGeeks
function renderHelloGeeksFun () public pure returns (string memory) {
return 'helloGeeks';
}
}
60
Explanation:
With reference to the attached snippet, we can see the code layout used here and can
verify the order of layout (i.e. Pragma statement followed by Contract and finally
Function definitions). Check whether the naming convention guidelines are also
followed or not and NatSpec declaration of comments is included or so.
This is a simple Solidity contract that defines a single function
called “renderHelloGeeksFun”. The function has no input parameters and returns a
string. The “pragma solidity” line at the top of the contract specifies the version of
Solidity that the contract is compatible with. In this case, the contract is compatible
with any version of Solidity between 0.8.6 and the latest version.
The “pure” keyword in the function declaration indicates that the function does not
modify the contract’s state and does not have any external side effects. This makes the
function cheaper to execute because it does not require any resources beyond the
computation of its return value.
The “returns” clause specifies the type and name of the value that the function returns.
In this case, the function returns a string called “string” and the data location is
“memory” for this string.
The function body consists of a single return statement that returns the
string “helloGeeks”. Overall, this contract defines a simple function that returns a fixed
string when it is called. It does not do anything else.
A naming convention is a set of rules for choosing names for variables, functions, and other
important entities in your code.
Naming conventions are powerful when adopted and used broadly. The use of different
conventions can convey significant meta information that would otherwise not be
immediately available.
The naming recommendations given here are intended to improve the readability, and thus
they are not rules, but rather guidelines to try and help convey the most information
through the names of things.
Lastly, consistency within a codebase should always supersede any conventions outlined
in this document.
Naming Styles
61
To avoid confusion, the following names will be used to refer to different naming styles.
b (single lowercase letter)
B (single uppercase letter)
lowercase
UPPERCASE
UPPER_CASE_WITH_UNDERSCORES
CapitalizedWords (or CapWords)
mixedCase (differs from CapitalizedWords by initial lowercase character!)
When using initialisms in CapWords, capitalize all the letters of the initialisms. Thus
HTTPServerError is better than HttpServerError. When using initialisms in mixedCase,
capitalize all the letters of the initialisms, except keep the first one lower case if it is the
beginning of the name. Thus xmlHTTPRequest is better than XMLHTTPRequest.
62
Use descriptive and meaningful names for modifiers, and indicate their purpose in the
name.
Use descriptive and meaningful names for enums, and indicate their purpose in the name.
Refer to the code snippet attached ahead for reference.
contract MyContract {
// Constants
uint256 public constant MIN_VALUE = 10;
uint256 public constant MAX_VALUE = 100;
// Variables
uint256 public totalSum;
address public owner;
mapping(address => uint256) public balances;
…
Names to Avoid
l - Lowercase letter el
O- Uppercase letter oh
I - Uppercase letter eye
Never use any of these for single letter variable names. They are often indistinguishable from
the numerals one and zero.
Contract and Library Names
Contracts and libraries should be named using the CapWords style.
Examples: SimpleToken, SmartBank, CertificateHashRepository, Player, Congress,
OwnedContract and library names should also match their filenames.
If a contract file includes multiple contracts and/or libraries, then the filename should
match the core contract. This is not recommended however if it can be avoided.
As shown in the example below, if the contract name is Congress and the library name
is Owned, then their associated filenames should be Congress.sol and Owned.sol.
63
Naming
Sl.No Description Examples
Convention
Structs should be named
1 Struct Names MyCoin, Position, PositionXY
using the CapWords style.
Events should be named Deposit, Transfer, Approval,
2 Event Names
using the CapWords style. BeforeTransfer, AfterTransfer
Functions should use getBalance, transfer, verifyOwner,
3 Function Names
mixedCase. addMember, changeOwner.
Function arguments should
use mixedCase.
When writing library
initialSupply, account,
Function functions that operate on a
4 recipientAddress,
Argument Names custom struct, the struct
senderAddress, newOwner.
should be the first argument
and should always be
named self.
totalSupply, remainingSupply,
Local and State
5 Use mixedCase. balancesOf, creatorAddress,
Variable Names
isPreSale, tokenExchangeRate.
Constants should be named
MAX_BLOCKS, TOKEN_NAME,
with all capital letters with
6 Constants TOKEN_TICKER,
underscores separating
CONTRACT_VERSION
words.
onlyBy, onlyAfter,
7 Modifier Names Use mixedCase.
onlyDuringThePreSale
Enums, in the style of
Enums simple type declarations, TokenGroup, Frame, HashStyle,
8
should be named using the CharacterLocation.
CapWords style.
_singleLeadingUnderscore
This convention is suggested for non-external functions and state variables
(private or internal). State variables without a specified visibility are internal by default.
When designing a smart contract, the public-facing API (functions that can be called by any
account) is an important consideration. Leading underscores allow you to immediately
recognize the intent of such functions, but more importantly, if you change a function from
non-external to external (including public) and rename it accordingly, this forces you to review
every call site while renaming. This can be an important manual check against unintended
64
external functions and a common source of security vulnerabilities (avoid find-replace-all
tooling for this change).
NatSpec
NatSpec (Natural Language Specification) is a system for documenting solidity code in a
way that is easy for humans to understand. NatSpec comments are written in Markdown
format and can be accessed through the Ethereum NatSpec Standard JSON Interface. To add
single-line NatSpec documentation to your Solidity code, use double-slash comments (‘//’)
followed by the NatSpec tag and the documentation text. And for adding multiple comments
(“/**…..*/”) Here’s an example to refer to while writing comments.
/**
@notice Description of the function
@param Parameter1 Description of parameter1
@param Parameter2 Description of parameter2
@return Description of the return value
*/
Below is the solidity code to demonstrate how to comment using the NatSpec format:
Solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.6 <0.9.0;
/// @title A contract for demonstrate NatSpec format
/// @author Jitendra Kumar
/// @notice For now, this contract just show how to comment using NatSpec format
contract helloGeeks {
/// @notice Return an initialized string
/// @return A string helloGeeks
function renderHelloGeeksFun () public pure returns (string memory) {
return 'helloGeeks';
}
}
65
Explanation:
With reference to the attached snippet, we can see the code layout used here and can
verify the order of layout (i.e. Pragma statement followed by Contract and finally
Function definitions). Check whether the naming convention guidelines are also followed
or not and NatSpec declaration of comments is included or so.
This is a simple Solidity contract that defines a single function
called “renderHelloGeeksFun”. The function has no input parameters and returns a
string. The “pragma solidity” line at the top of the contract specifies the version of
Solidity that the contract is compatible with. In this case, the contract is compatible with
any version of Solidity between 0.8.6 and the latest version.
The “pure” keyword in the function declaration indicates that the function does not
modify the contract’s state and does not have any external side effects. This makes the
function cheaper to execute because it does not require any resources beyond the
computation of its return value.
The “returns” clause specifies the type and name of the value that the function returns.
In this case, the function returns a string called “string” and the data location is
“memory” for this string.
The function body consists of a single return statement that returns the
string “helloGeeks”. Overall, this contract defines a simple function that returns a fixed
string when it is called. It does not do anything else.
The recommended method of sending funds after an effect is using the withdrawal pattern.
Although the most intuitive method of sending Ether, as a result of an effect, is a
direct transfer call, this is not recommended as it introduces a potential security risk. You may
read more about this on the Security Considerations page.
The following is an example of the withdrawal pattern in practice in a contract where the goal is
to send the most of some compensation, e.g. Ether, to the contract in order to become the “richest”,
inspired by King of the Ether.
In the following contract, if you are no longer the richest, you receive the funds of the person who
is now the richest.
66
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.4;
contract WithdrawalContract {
address public richest;
uint public mostSent;
constructor() payable {
richest = msg.sender;
mostSent = msg.value;
}
contract SendContract {
address payable public richest;
uint public mostSent;
constructor() payable {
richest = payable(msg.sender);
mostSent = msg.value;
}
Notice that, in this example, an attacker could trap the contract into an unusable state
by causing richest to be the address of a contract that has a receive or fallback function
67
which fails (e.g. by using revert() or by just consuming more than the 2300 gas stipend
transferred to them). That way, whenever transfer is called to deliver funds to the
“poisoned” contract, it will fail and thus also becomeRichest will fail, with the contract
being stuck forever.
In contrast, if you use the “withdraw” pattern from the first example, the attacker can
only cause his or her own withdraw to fail and not the rest of the contract’s workings.
Restricting Access
Restricting access is a common pattern for contracts. Note that you can never
restrict any human or computer from reading the content of your transactions or
your contract’s state. You can make it a bit harder by using encryption, but if your
contract is supposed to read the data, so will everyone else.
You can restrict read access to your contract’s state by other contracts. That is
actually the default unless you declare your state variables public.
Furthermore, you can restrict who can make modifications to you r contract’s state
or call your contract’s functions and this is what this section is about.
The use of function modifiers makes these restrictions highly readable.
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.4;
contract AccessRestriction {
// These will be assigned at the construction
// phase, where `msg.sender` is the account
// creating this contract.
address public owner = msg.sender;
uint public creationTime = block.timestamp;
68
{
if (msg.sender != account)
revert Unauthorized();
// Do not forget the "_;"! It will
// be replaced by the actual function
// body when the modifier is used.
_;
}
_;
if (msg.value > amount)
payable(msg.sender).transfer(msg.value - amount);
}
69
5.9.2. State Machine
Contracts often act as a state machine, which means that they have certain stages in which
they behave differently or in which different functions can be called. A function call often
ends a stage and transitions the contract into the next stage (especially if the contract
models interaction). It is also common that some stages are automatically reached at a
certain point in time.
An example for this is a blind auction contract which starts in the stage “accepting blinded
bids”, then transitions to “revealing bids” which is ended by “determine auction outcome”.
Function modifiers can be used in this situation to model the states and guard against
incorrect usage of the contract.
Example
In the following example, the modifier atStage ensures that the function can only
be called at a certain stage.
Automatic timed transitions are handled by the modifier timedTransitions , which
should be used for all functions.
Modifier Order Matters . If atStage is combined with timedTransitions, make
sure that you mention it after the latter, so that the new stage is taken into
account.
Finally, the modifier transitionNext can be used to automatically go to the next
stage when the function finishes.
Modifier May be Skipped . This only applies to Solidity before version 0.4.0:
Since modifiers are applied by simply replacing code and not by using a function
call, the code in the transitionNext modifier can be skipped if the function itself
uses return. If you want to do that, make sure to call nextStage man ually from
those functions. Starting with version 0.4.0, modifier code will run even if the
function explicitly returns.
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.4;
contract StateMachine {
enum Stages {
AcceptingBlindedBids,
RevealBids,
AnotherStage,
70
AreWeDoneYet,
Finished
}
/// Function cannot be called at this time.
error FunctionInvalidAtThisStage();
function reveal()
public
timedTransitions
atStage(Stages.RevealBids)
{
}
function g()
public
timedTransitions
atStage(Stages.AnotherStage)
transitionNext
{
71
}
function h()
public
timedTransitions
atStage(Stages.AreWeDoneYet)
transitionNext
{
}
function i()
public
timedTransitions
atStage(Stages.Finished)
{
}
}
References:
1. https://medium.com/@solidity101/solidity-and-web3-js-building-user-interfaces-for-
dapps-605bfad343db
2. https://www.geeksforgeeks.org/difference-between-dapps-crypto-wallets-and-smart-
contracts/?ref=ml_lbp
3. https://www.geeksforgeeks.org/5-best-frameworks-for-developing-decentralized-
applications/?ref=ml_lbp
4. https://zebpay.com/in/blog/difference-between-smart-contract-vs-blockchain
5. https://www.geeksforgeeks.org/smart-contracts-in-blockchain/
6. https://medium.com/@blocktorch/architecture-of-decentralized-applications-dapps-
d583db198a6f
7. https://www.geeksforgeeks.org/introduction-to-solidity/
8. https://docs.soliditylang.org/en/latest/solidity-by-example.html
72