0% found this document useful (0 votes)
7 views72 pages

Unit5 SCSA1615 SmartContract Final

Unit V of the B.E-CSE Blockchain Technology course focuses on decentralized applications (dApps), covering their architecture, coding details, and design patterns. It discusses components such as frontends, hosting, wallets, nodes, and smart contracts, emphasizing the importance of libraries like Web3.js for blockchain interactions. The unit also highlights the role of oracles, indexing solutions, and data storage in enhancing dApp functionality and efficiency.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
7 views72 pages

Unit5 SCSA1615 SmartContract Final

Unit V of the B.E-CSE Blockchain Technology course focuses on decentralized applications (dApps), covering their architecture, coding details, and design patterns. It discusses components such as frontends, hosting, wallets, nodes, and smart contracts, emphasizing the importance of libraries like Web3.js for blockchain interactions. The unit also highlights the role of oracles, indexing solutions, and data storage in enhancing dApp functionality and efficiency.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 72

III YEAR - SEMESTER VI

B.E-CSE (BLOCKCHAIN TECHNOLOGY)


SCSA1615- SMART CONTRACT
UNIT V DECENTRALIZED APPS – CODING DETAILS, STYLE GUIDE AND
DESIGN PATTERNS

Unit V: Decentralized Application Architecture – Connecting to the Blockchain and Smart


Contract – Web3js, Deployment Sample web pages (HTML/CSS/JavaScript), Voting Contract
and App – Blind Auction Contract and Safe Remote Purchase Contract and App, Micro payment
Channel Contract and App, Coding Style Guide Code Layout, Naming Conventions, Common
Design Patterns, Withdrawal from Contracts, State Machine.

5.1. Decentralized Application Architecture

Architecture of Decentralized Applications (dApps)


Decentralized applications (dApps) are transforming the digital landscape by offering users
autonomy, security, and transparency. Unlike traditional applications that rely on centralized
servers, dApps are (partly or entirely) built on decentralized technology. In this article, we’ll
explore the full stack architecture of a dApp, encompassing the frontend, hosting, wallets, smart
contracts, access to nodes, data storage, and other potential components. Each layer can fill out
a full blog by itself, but we try to summarize the full stack comprehensively.

Fig. Architecture of Decentralized Applications (dApps)

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

What are Blockchains?


Blockchains are a new way to efficiently store and access data. Also known as distributed
ledger technology, blockchains are publicly available records of transactions that can be
accessed by anyone, anywhere.
A blockchain is thus a shared ledger of transactions where data cannot be altered or deleted
once created. It provides a transparent and accessible method to track information, along with
the guarantee that the data available is accurate and has not been changed.
What are Smart Contracts?
Smart contracts can be thought of as software programs on a blockchain. They automatically
run when their conditions are met. They can be used by blockchain projects to automate tasks
such as agreements and the transfer of assets without the need for a trusted third party.
How does Blockchain Work?
Distributed Ledger Technology
A key aspect of blockchains is their decentralisation. The database is not stored on one single
system. Instead, it is available to any device on the network. All blockchain users can freely
access the network and its information.
Blockchains do not process each transaction individually. They are recorded as encrypted
“blocks” of data that must be validated by other users before being finalised. Each block’s
encryption is based on the block that came before it, thus forming a chain.
Blockchain Use Cases
One of the most popular uses for blockchains is crypto. Crypto tokens are a form of digital
asset. They are powered by the blockchain as this is what allows crypto transactions to be
recorded and validated.
Alternatively, blockchains can also be used for secure data storage by companies. In this
situation, companies can operate a private blockchain where data is stored across multiple
different computers and locations. Access to the network can be restricted to authorized users,
ensuring sensitive information is kept private.

How Do Smart Contracts Work?


Execution of Smart Contracts
As mentioned above, smart contracts are sets of code that execute automatically when the
requirements are met. To create a smart contract, you must have some programming

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. 

 Decentralised – Both technologies operate


 Ease of Use – It is easy to get started on

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.

How Do Smart Contracts Work with Blockchain?


A smart contract is a sort of program that encodes business logic and operates on a dedicated
virtual machine embedded in a blockchain or other distributed ledger.
Step 1: Business teams collaborate with developers to define their criteria for the smart
contract's desired behavior in response to certain events or circumstances.
Step 2: Conditions such as payment authorization, shipment receipt, or a utility meter reading
threshold are examples of simple events.

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.

5.3. Web3js, Deployment Sample web pages (HTML/CSS/JavaScript)


5.3.1 Web3js:
 Web 3.0 or web3 is a lucrative domain for app development. It is the third generation of
the web and introduces the concept of a decentralized and semantic web. Therefore, the
use of libraries such as web3.js has become important for blockchain developers.
 In simple words, the definition of web3.js would paint it as a JavaScript library you can
use for interactions with the Ethereum blockchain. The following post will help you learn
more about the web3.js tutorial in detail and the different actions you can take to use
web3.js for dApps.

Fig. Architecture of Web3.js

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.

 DAO does not require a central authority for authentication or validation.


Cryptocurrencies and blockchains are technologies that follow DAO principles.
Web 3.0 has some distinctive features:
Decentralization:
This is the core idea of Web 3.0. In Web 2.0, computers use HTTP in the form of unique web
addresses to find information stored in a fixed location, usually a single server. Web 3.0 finds
information based on its content, so it can be stored and distributed in multiple places at the
same time. This would collapse the massive databases currently maintained by internet giants
such as Meta and Google, giving users more control.
Trustless and Permissionless:
In addition to being decentralized and based on open-source software, Web 3.0 is trustless
(meaning a network in which participants can interact directly without a trusted intermediary)
and permissionless ((meaning anyone can join without permission). ). As a result, Web 3. 0
applications run on blockchains or decentralized peer-to-peer networks.

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

Reading Smart Contract Data with Web3.js


 You can read smart contract data with the help of a JavaScript version of the smart
contract you want to read. The second important requirement for reading smart contract
data refers to the method for calling smart contract functions during data reading.
 You can create a JavaScript version of smart contract by using the “web3.eth.Contract()”
function. The function would need two specific arguments, with one intended for smart
contract ABI and the other one for smart contract address.
 Smart contract ABI or Abstract Binary Interface is practically a JSON array providing
description of the working of a particular smart contract. Once you have configured the
smart contract ABI, you could work on generating a comprehensive JavaScript
representation of the smart contract.
 In the next step of this web3 JS example, you must call smart contract functions for
reading data. You can find all smart contract functions listed in the ‘contract.methods’
namespace for the concerned web3 contract. Therefore, the function can help you in
calling any function you can implement with the smart contract.

5.3.2. Deployment Sample web pages (HTML/CSS/JavaScript)

5.3.2.1. How to Calling Smart Contract Functions with Web3.js?


Web3.js:
Web3. js is a collection of libraries that allow users to interact between smart contracts and
the front end. It is used to develop websites that can interact with different blockchains.
Ethereum-based applications can interact with developers through a set of APIs provided by
Web3.js. These APIs include:
 Web3: It is the main interface for interacting with Ethereum Blockchain. It provides
functions for interacting with contracts, accounts, and the Ethereum main network or test
network.

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

if (typeof web3 !== 'undefined'){


web3 = new Web3(web3.currentProvider);
}
else{
// Set the provider you want from Web3.providers
web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
}
// Set the 1st account for transactions
web3.eth.defaultAccount = web3.eth.accounts[0];
// Set the ABI
var yourContract = web3.eth.contract('PASTE ABI HERE!');

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
}

// This is a type for a single proposal.


struct Proposal {
bytes32 name; // short name (up to 32 bytes)
uint voteCount; // number of accumulated votes
}

address public chairperson;

// This declares a state variable that


// stores a `Voter` struct for each possible address.
mapping(address => Voter) public voters;

// A dynamically-sized array of `Proposal` structs.


Proposal[] public proposals;

/// Create a new ballot to choose one of `proposalNames`.


constructor(bytes32[] memory proposalNames) {
chairperson = msg.sender;
voters[chairperson].weight = 1;

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
}));
}
}

// Give `voter` the right to vote on this ballot.


// May only be called by `chairperson`.
function giveRightToVote(address voter) external {
// If the first argument of `require` evaluates
// to `false`, execution terminates and all
// changes to the state and to Ether balances
// are reverted.
// This used to consume all gas in old EVM versions, but
// not anymore.
// It is often a good idea to use `require` to check if
// functions are called correctly.
// As a second argument, you can also provide an
// explanation about what went wrong.
require(
msg.sender == chairperson,
"Only chairperson can give right to vote."
);
require(
!voters[voter].voted,
"The voter already voted."
);
require(voters[voter].weight == 0);
voters[voter].weight = 1;
}

/// Delegate your vote to the voter `to`.


function delegate(address to) external {
// assigns reference
Voter storage sender = voters[msg.sender];
require(sender.weight != 0, "You have no right to vote");
require(!sender.voted, "You already voted.");

require(to != msg.sender, "Self-delegation is disallowed.");

// Forward the delegation as long as


// `to` also delegated.
// In general, such loops are very dangerous,
// because if they run too long, they might
// need more gas than is available in a block.
// In this case, the delegation will not be executed,

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;

// We found a loop in the delegation, not allowed.


require(to != msg.sender, "Found loop in delegation.");
}

Voter storage delegate_ = voters[to];

// Voters cannot delegate to accounts that cannot vote.


require(delegate_.weight >= 1);

// Since `sender` is a reference, this


// modifies `voters[msg.sender]`.
sender.voted = true;
sender.delegate = to;

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;
}
}

/// Give your vote (including votes delegated to you)


/// to proposal `proposals[proposal].name`.
function vote(uint proposal) external {
Voter storage sender = voters[msg.sender];
require(sender.weight != 0, "Has no right to vote");
require(!sender.voted, "Already voted.");
sender.voted = true;
sender.vote = proposal;

// If `proposal` is out of the range of the array,


// this will throw automatically and revert all
// changes.
proposals[proposal].voteCount += sender.weight;
}

/// @dev Computes the winning proposal taking all


/// previous votes into account.
function winningProposal() public view
returns (uint winningProposal_)
{
uint winningVoteCount = 0;
for (uint p = 0; p < proposals.length; p++) {
if (proposals[p].voteCount > winningVoteCount) {
winningVoteCount = proposals[p].voteCount;
winningProposal_ = p;

25
}
}
}

// Calls winningProposal() function to get the index


// of the winner contained in the proposals array and then
// returns the name of the winner
function winnerName() external view
returns (bytes32 winnerName_)
{
winnerName_ = proposals[winningProposal()].name;
}
}

5.5. Blind Auction Contract and Safe Remote Purchase Contract and App

5.5.1. Blind Auction


In this section, show how to create a completely blind auction contract on Ethereum. We will start
with an open auction where everyone can see the bids that are made and then extend this contract
into a blind auction where it is not possible to see the actual bid until the bidding period ends.
Simple Open Auction
The general idea of the following simple auction contract is that everyone can send their bids
during a bidding period. The bids already include sending some compensation, e.g. Ether, in order
to bind the bidders to their bid. If the highest bid is raised, the previous highest bidder gets their
Ether back. After the end of the bidding period, the contract has to be called manually for the
beneficiary to receive their Ether - contracts cannot activate themselves.
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.4;
contract SimpleAuction {
// Parameters of the auction. Times are either
// absolute unix timestamps (seconds since 1970-01-01)
// or time periods in seconds.
address payable public beneficiary;
uint public auctionEndTime;

// Current state of the auction.


address public highestBidder;
uint public highestBid;

// Allowed withdrawals of previous bids


mapping(address => uint) pendingReturns;

// Set to true at the end, disallows any change.


// By default initialized to `false`.
bool ended;

// Events that will be emitted on changes.


event HighestBidIncreased(address bidder, uint amount);
event AuctionEnded(address winner, uint amount);

// Errors that describe failures.

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.

/// The auction has already ended.


error AuctionAlreadyEnded();
/// There is already a higher or equal bid.
error BidNotHighEnough(uint highestBid);
/// The auction has not ended yet.
error AuctionNotYetEnded();
/// The function auctionEnd has already been called.
error AuctionEndAlreadyCalled();

/// Create a simple auction with `biddingTime`


/// seconds bidding time on behalf of the
/// beneficiary address `beneficiaryAddress`.
constructor(
uint biddingTime,
address payable beneficiaryAddress
) {
beneficiary = beneficiaryAddress;
auctionEndTime = block.timestamp + biddingTime;
}

/// Bid on the auction with the value sent


/// together with this transaction.
/// The value will only be refunded if the
/// auction is not won.
function bid() external payable {
// No arguments are necessary, all
// information is already part of
// the transaction. The keyword payable
// is required for the function to
// be able to receive Ether.

// Revert the call if the bidding


// period is over.
if (block.timestamp > auctionEndTime)
revert AuctionAlreadyEnded();

// If the bid is not higher, send the


// Ether back (the revert statement
// will revert all changes in this
// function execution including
// it having received the Ether).
if (msg.value <= highestBid)
revert BidNotHighEnough(highestBid);

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);
}

/// Withdraw a bid that was overbid.


function withdraw() external returns (bool) {
uint amount = pendingReturns[msg.sender];

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;

// msg.sender is not of type `address payable` and must be


// explicitly converted using `payable(msg.sender)` in order
// use the member function `send()`.
if (!payable(msg.sender).send(amount)) {
// No need to call throw here, just reset the amount owing
pendingReturns[msg.sender] = amount;
return false;
}
}
return true;
}

/// End the auction and send the highest bid


/// to the beneficiary.
function auctionEnd() external {
// It is a good guideline to structure functions that interact
// with other contracts (i.e. they call functions or send Ether)
// into three phases:
// 1. checking conditions
// 2. performing actions (potentially changing conditions)
// 3. interacting with other contracts
// If these phases are mixed up, the other contract could call
// back into the current contract and modify the state or cause
// effects (ether payout) to be performed multiple times.
// If functions called internally include interaction with external
// contracts, they also have to be considered interaction with
// external contracts.

// 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;
}

address payable public beneficiary;


uint public biddingEnd;
uint public revealEnd;
bool public ended;

mapping(address => Bid[]) public bids;

address public highestBidder;


uint public highestBid;

// Allowed withdrawals of previous bids


mapping(address => uint) pendingReturns;

event AuctionEnded(address winner, uint highestBid);

// Errors that describe failures.

/// The function has been called too early.


/// Try again at `time`.
error TooEarly(uint time);
/// The function has been called too late.
/// It cannot be called after `time`.
error TooLate(uint time);
/// The function auctionEnd has already been called.
error AuctionEndAlreadyCalled();

// Modifiers are a convenient way to validate inputs to


// functions. `onlyBefore` is applied to `bid` below:
// The new function body is the modifier's body where
// `_` is replaced by the old function body.
modifier onlyBefore(uint time) {
if (block.timestamp >= time) revert TooLate(time);
_;
}

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;
}

/// Place a blinded bid with `blindedBid` =


/// keccak256(abi.encodePacked(value, fake, secret)).
/// The sent ether is only refunded if the bid is correctly
/// revealed in the revealing phase. The bid is valid if the
/// ether sent together with the bid is at least "value" and
/// "fake" is not true. Setting "fake" to true and sending
/// not the exact amount are ways to hide the real bid but
/// still make the required deposit. The same address can
/// place multiple bids.
function bid(bytes32 blindedBid)
external
payable
onlyBefore(biddingEnd)
{
bids[msg.sender].push(Bid({
blindedBid: blindedBid,
deposit: msg.value
}));
}

/// 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);
}

/// Withdraw a bid that was overbid.


function withdraw() external {
uint amount = pendingReturns[msg.sender];
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 `transfer` returns (see the remark above about
// conditions -> effects -> interaction).
pendingReturns[msg.sender] = 0;

payable(msg.sender).transfer(amount);
}
}

/// End the auction and send the highest bid


/// to the beneficiary.
function auctionEnd()
external
onlyAfter(revealEnd)
{
if (ended) revert AuctionEndAlreadyCalled();
emit AuctionEnded(highestBidder, highestBid);
ended = true;
beneficiary.transfer(highestBid);
}

// This is an "internal" function which means that it


// can only be called from the contract itself (or from
// derived contracts).
function placeBid(address bidder, uint value) internal
returns (bool success)
{
if (value <= highestBid) {
return false;
}
if (highestBidder != address(0)) {
// Refund the previously highest bidder.
pendingReturns[highestBidder] += highestBid;
}
highestBid = value;
highestBidder = bidder;
return true;
}
}

5.5.2. Safe Remote Purchase


 Purchasing goods remotely currently requires multiple parties that need to trust each other.
The simplest configuration involves a seller and a buyer. The buyer would like to receive an
item from the seller and the seller would like to get some compensation, e.g. Ether, in return.
The problematic part is the shipment here: There is no way to determine for sure that the item
arrived at the buyer.

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;

enum State { Created, Locked, Release, Inactive }


// The state variable has a default value of the first member, `State.created`
State public state;

modifier condition(bool condition_) {


require(condition_);
_;
}

/// Only the buyer can call this function.


error OnlyBuyer();
/// Only the seller can call this function.
error OnlySeller();
/// The function cannot be called at the current state.
error InvalidState();
/// The provided value has to be even.
error ValueNotEven();

modifier onlyBuyer() {
if (msg.sender != buyer)
revert OnlyBuyer();
_;
}

modifier onlySeller() {
if (msg.sender != seller)
revert OnlySeller();
_;
}

modifier inState(State state_) {


if (state != state_)
revert InvalidState();
_;
}

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();
}

/// Abort the purchase and reclaim the ether.


/// Can only be called by the seller before
/// the contract is locked.
function abort()
external
onlySeller
inState(State.Created)
{
emit Aborted();
state = State.Inactive;
// We use transfer here directly. It is
// reentrancy-safe, because it is the
// last call in this function and we
// already changed the state.
seller.transfer(address(this).balance);
}

/// Confirm the purchase as buyer.


/// Transaction has to include `2 * value` ether.
/// The ether will be locked until confirmReceived
/// is called.
function confirmPurchase()
external
inState(State.Created)
condition(msg.value == (2 * value))
payable
{
emit PurchaseConfirmed();
buyer = payable(msg.sender);
state = State.Locked;
}

/// Confirm that you (the buyer) received the item.


/// This will release the locked ether.
function confirmReceived()
external
onlyBuyer
inState(State.Locked)
{
emit ItemReceived();
// It is important to change the state first because
// otherwise, the contracts called using `send` below
// can call in again here.
state = State.Release;

buyer.transfer(value);
}

/// This function refunds the seller, i.e.


/// pays back the locked funds of the seller.
function refundSeller()
external
onlySeller
inState(State.Release)
{

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);
}
}

5.6. Micro payment Channel Contract and App

A payment channel which uses cryptographic signatures to make repeated transfers o f


Ether between the same parties secure, instantaneous, and without transaction fees. For
the example, we need to understand how to sign and verify signatures, and setup the
payment channel.
Creating and verifying signatures
 Imagine Alice wants to send some Ether to Bob, i.e. Alice is the sender and Bob is
the recipient.
 Alice only needs to send cryptographically signed messages off-chain (e.g. via
email) to Bob and it is similar to writing checks.
 Alice and Bob use signatures to authorize transactions, which is possible with
smart contracts on Ethereum. Alice will build a simple smart contract that lets her
transmit Ether, but instead of calling a function herself to initiate a payment, she
will let Bob do that, and therefore pay the transaction fee.
The contract will work as follows:
1. Alice deploys the ReceiverPays contract, attaching enough Ether to cover the
payments that will be made.
2. Alice authorizes a payment by signing a message with her private key.
3. Alice sends the cryptographically signed message to Bob. The message does not
need to be kept secret (explained later), and the mechanism for sending it does
not matter.
4. Bob claims his payment by presenting the signed message to the smart contract,
it verifies the authenticity of the message and then releases the funds.

Creating the signature


Alice does not need to interact with the Ethereum network to sign the transaction, the
process is completely offline. In this tutorial, we will sign messages in the browser

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:

1. The recipient’s address.


2. The amount to be transferred.
3. Protection against replay attacks.

 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:

// recipient is the address that should be paid.


// amount, in wei, specifies how much ether should be sent.
// nonce can be any unique number to prevent replay attacks
// contractAddress is used to prevent cross-contract replay attacks
function signPayment(recipient, amount, nonce, contractAddress, callback) {
var hash = "0x" + abi.soliditySHA3(
["address", "uint256", "uint256", "address"],
[recipient, amount, nonce, contractAddress]
).toString("hex");

web3.eth.personal.sign(hash, web3.eth.defaultAccount, callback);


}

Recovering the Message Signer in Solidity


In general, ECDSA signatures consist of two parameters, r and s. Signatures in Ethereum
include a third parameter called v, that you can use to verify which account’s private key was
used to sign the message, and the transaction’s sender. Solidity provides a built-in
function ecrecover that accepts a message along with the r, s and v parameters and returns the
address that was used to sign the message.
Extracting the Signature Parameters
Signatures produced by web3.js are the concatenation of r, s and v, so the first step is to split
these parameters apart. You can do this on the client-side, but doing it inside the smart contract
means you only need to send one signature parameter rather than three. Splitting apart a byte
array into its constituent parts is a mess, so we use inline assembly to do the job in
the splitSignature function (the third function in the full contract at the end of this section).

Computing the Message Hash


The smart contract needs to know exactly what parameters were signed, and so it must recreate
the message from the parameters and use that for signature verification. The
functions prefixed and recoverSigner do this in the claimPayment function.

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.

What is a Payment Channel?


Payment channels allow participants to make repeated transfers of Ether without using
transactions. This means that you can avoid the delays and fees associated with transactions.
We are going to explore a simple unidirectional payment channel between two parties (Alice
and Bob). It involves three steps:
1. Alice funds a smart contract with Ether. This “opens” the payment channel.
2. Alice signs messages that specify how much of that Ether is owed to the recipient. This
step is repeated for each payment.
3. Bob “closes” the payment channel, withdrawing his portion of the Ether and sending the
remainder back to the sender.
Only steps 1 and 3 require Ethereum transactions, step 2 means that the sender transmits a
cryptographically signed message to the recipient via off chain methods (e.g. email). This
means only two transactions are required to support any number of transfers.
Bob is guaranteed to receive his funds because the smart contract escrows the Ether and
honours a valid signed message. The smart contract also enforces a timeout, so Alice is
guaranteed to eventually recover her funds even if the recipient refuses to close the channel. It
is up to the participants in a payment channel to decide how long to keep it open. For a short-
lived transaction, such as paying an internet café for each minute of network access, the
payment channel may be kept open for a limited duration. On the other hand, for a recurring
payment, such as paying an employee an hourly wage, the payment channel may be kept open
for several months or years.
Opening the Payment Channel

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]
);
}

function signMessage(message, callback) {


web3.eth.personal.sign(
"0x" + message.toString("hex"),
web3.eth.defaultAccount,
callback
);
}

// contractAddress is used to prevent cross-contract replay attacks.


// amount, in wei, specifies how much Ether should be sent.

function signPayment(contractAddress, amount, callback) {


var message = constructPaymentMessage(contractAddress, amount);
signMessage(message, callback);
}
Closing the Payment Channel
 When Bob is ready to receive his funds, it is time to close the payment channel by calling
a close function on the smart contract. Closing the channel pays the recipient the Ether
they are owed and deactivates the contract by freezing it, sending any remaining Ether
back to Alice. To close the channel, Bob needs to provide a message signed by Alice.

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.");
_;
}

function freeze() internal {


_frozen = true;
}
}

contract SimplePaymentChannel is Frozeable {


address payable public sender; // The account sending payments.
address payable public recipient; // The account receiving the payments.
uint256 public expiration; // Timeout in case the recipient never closes.

constructor (address payable recipientAddress, uint256 duration)


payable
{
sender = payable(msg.sender);
recipient = recipientAddress;

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);
}

/// the sender can extend the expiration at any time


function extend(uint256 newExpiration)
external
notFrozen
{
require(msg.sender == sender);
require(newExpiration > expiration);

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);
}

function isValidSignature(uint256 amount, bytes memory signature)


internal
view
returns (bool)
{
bytes32 message = prefixed(keccak256(abi.encodePacked(this, amount)));
// check that the signature is from the payment sender
return recoverSigner(message, signature) == sender;
}

/// 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);
}

function recoverSigner(bytes32 message, bytes memory sig)


internal
pure
returns (address)
{
(uint8 v, bytes32 r, bytes32 s) = splitSignature(sig);
return ecrecover(message, v, r, s);
}

/// builds a prefixed hash to mimic the behavior of eth_sign.


function prefixed(bytes32 hash) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
}
}

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]
);
}

function recoverSigner(message, signature) {


var split = ethereumjs.Util.fromRpcSig(signature);
var publicKey = ethereumjs.Util.ecrecover(message, split.v, split.r, split.s);
var signer = ethereumjs.Util.pubToAddress(publicKey).toString("hex");
return signer;
}

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;

event Transfer(address from, address to, uint amount);


event Approval(address owner, address spender, uint amount);

function transfer(address to, uint amount) external returns (bool success) {


balances.move(msg.sender, to, amount);
emit Transfer(msg.sender, to, amount);
return true;

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;
}

function approve(address spender, uint tokens) external returns (bool success) {


require(allowed[msg.sender][spender] == 0, "");
allowed[msg.sender][spender] = tokens;
emit Approval(msg.sender, spender, tokens);
return true;
}

function balanceOf(address tokenOwner) external view returns (uint balance) {


return balances[tokenOwner];
}
}

5.7. Coding Style Guide Code Layout

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.

3. Maximum Line Length


Limit Line Length to 120 Characters
If a line of code exceeds this limit, consider breaking it up into multiple lines. This makes it
easier to read the code even on smaller screens.
Wrapped lines should confirm to the following guidelines.
 The first argument should not be attached to the opening parenthesis.
 One, and only one, indent should be used.
 Each argument should fall on its own line.
 The terminating element, );, should be placed on the final line by itself.
Refer to the right and wrong snippets written ahead to understand better.
// right approach
thisFunctionCallIsReallyLong(
tempArgument1,
tempArgument2,
tempArgument3
);
// Wrong approaches
thisFunctionCallIsReallyLong(tempArgument1,
44
tempArgument2,
tempArgument3
);
thisFunctionCallIsReallyLong(tempArgument1,
tempArgument2,
tempArgument3
);
thisFunctionCallIsReallyLong(
tempArgument1, tempArgument2,
tempArgument3
);

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
// … {
} // …
}

5. Blank Line Between Functions


Surround top level declarations in solidity source with two blank lines. Add a blank line
between the end of a function and the beginning of the next function. Refer to the right and
wrong snippets written ahead to understand better.

pragma solidity >=0.6.0 <0.9.0;


// right practice
abstract contract A {
function spam() public virtual pure;
function ham() public virtual pure;
45
}
contract B is A {
function spam() public pure override {
// …
}
function ham() public pure override {
// …
}
}
pragma solidity >=0.6.0 <0.9.0;
// wrong practice
abstract contract A {
function spam() virtual pure public;
function ham() public virtual pure;
}
contract B is A {
function spam() public pure override {
// …
}
function ham() public pure override {
// …
}
}
6. Blank Line Between Different Blocks of Code
Add a blank line between different blocks of code within a function (e.g. between variable
declarations and function calls). Refer to the right and wrong snippets written ahead to
understand better.
// right practice // wrong practice
uint256 public jobfirst; uint256 public jobfirst;
doSomething(); doSomething();

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;

8. No Space Between Reserved Keywords


While using reserved keywords never add spaces in between. Refer to the right and wrong
snippets written ahead to understand better.
// right practice
mapping(uint => uint) map;
mapping(address => bool) registeredAddresses;
mapping(uint => mapping(uint => s)) data;
// wrong practice
mapping (uint => uint) map;
mapping( address => bool ) registeredAddresses;
mapping(uint => mapping (uint => s)) data;

9. Source File Encoding


UTF-8 or ASCII encoding is preferred.

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;

contract Coin { contract Coin


struct Bank { {
address owner; struct Bank {
uint balance; address owner;
} uint balance;
} }
}
 The same recommendations apply to the control structures if, else, while, and for.
 Additionally, there should be a single space between the control structures if, while,
and for and the parenthetic block representing the conditional, as well as a single space
between the conditional parenthetic block and the opening brace.

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;

14. Function Declaration


Declare functions using the keyword function, followed by the function name, a list of
parameters in parentheses (), and the function body in curly braces {}. Avoid declaring
functions with the same name, even if they have different parameters, as this can cause
confusion.
 For short function declarations, it is recommended for the opening brace of the
function body to be kept on the same line as the function declaration.
 The closing brace should be at the same indentation level as the function
declaration.
 The opening brace should be preceded by a single space

// 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 increment(uint x) public onlyOwner pure returns (uint) {


return x + 1;
}
No:
function balance(uint from) public override view returns (uint) {
return balanceOf[from];
}

function increment(uint x) onlyOwner public pure returns (uint) {


return x + 1;
}
For long function declarations, it is recommended to drop each argument onto its own line at
the same indentation level as the function body. The closing parenthesis and opening bracket
should be placed on their own line as well at the same indentation level as the function
declaration
Yes:
function thisFunctionHasLotsOfArguments(
address a,
address b,
address c,
address d,
address e,
address f
)
public
{
doSomething();
}
No:
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();
}

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();
}

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();
}

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
);
}

For constructor functions on inherited contracts whose bases require arguments, it is


recommended to drop the base constructors onto new lines in the same manner as modifiers
if the function declaration is long or hard to read.
Yes:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
// Base contracts just to make this compile
contract B {
constructor(uint) {
}
}

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;

// Base contracts just to make this compile


contract B {
constructor(uint) {
}
}

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;
}
}

When declaring short functions with a single statement, it is permissible to do it on a single


line.
Permissible:
function shortFunction() public { doSomething(); }
These guidelines for function declarations are intended to improve readability.
15. Mappings
In variable declarations, do not separate the keyword mapping from its type by a space. Do
not separate any nested mapping keyword from its type by whitespace.
Yes:
mapping(uint => uint) map;
mapping(address => bool) registeredAddresses;
mapping(uint => mapping(bool => Data[])) public data;
mapping(uint => mapping(uint => s)) data;
No:
mapping (uint => uint) map;
mapping( address => bool ) registeredAddresses;
mapping (uint => mapping (bool => Data[])) public data;
mapping(uint => mapping (uint => s)) data;

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;
}

17. Names to Avoid


Avoid using keywords (e.g. contract, function, etc.) as names for variables, functions, or
contracts. Avoid using single-letter names, except for loop indices.
// Good
function addNumbers(uint a, uint b) public returns (uint) { … }
for (uint i = 0; i < 10; i++) { … }
// Bad
function contract(uint a, uint b) public returns (uint) { … }
for (uint x = 0; x < 10; x++) { … }
18. Contracts and Library Names
Name contracts and libraries in a descriptive manner that accurately reflects their purpose.
Avoid naming contracts and libraries after their dependencies.
// Good // Bad
contract Token { … } contract ERC20 { … }
library Math { … } library TokenLibrary { … }

19. Other Recommendations


 Strings should be quoted with double-quotes instead of single-quotes.
Yes:
str = "foo";
str = "Hamlet says, 'To be or not to be...'";

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.

8. Public and External Function Definitions


Declare all public and external functions next. Public functions can be called by any contract
or external actor, while external functions can only be called by other contracts. It is good
practice to group related functions together and to place functions that are more frequently
used towards the top.
9. Internal Functions
Declare any internal functions next. Internal functions can only be called by other functions
within the same contract and are useful for encapsulating complex logic or for sharing code
between functions.
10. Private Functions
Declare any private functions last. Private functions can only be called by other functions
within the same contract and are useful for encapsulating sensitive logic or for sharing code
between functions in a way that is not visible to external actors.

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.

5.8. Naming Conventions

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.

Rules/Guidelines for the Naming Conventions


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

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.

Avoiding Naming Collisions


 singleTrailingUnderscore_
This convention is suggested when the desired name collides with that of an existing state
variable, function, built-in or otherwise reserved name.
Underscore Prefix for Non-external Functions and Variables

 _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.

5.9. Common Design Patterns

5.9.1. Withdrawal from Contracts

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;

mapping(address => uint) pendingWithdrawals;

/// The amount of Ether sent was not higher than


/// the currently highest amount.
error NotEnoughEther();

constructor() payable {
richest = msg.sender;
mostSent = msg.value;
}

function becomeRichest() public payable {


if (msg.value <= mostSent) revert NotEnoughEther();
pendingWithdrawals[richest] += msg.value;
richest = msg.sender;
mostSent = msg.value;
}

function withdraw() public {


uint amount = pendingWithdrawals[msg.sender];
// Remember to zero the pending refund before
// sending to prevent reentrancy attacks
pendingWithdrawals[msg.sender] = 0;
payable(msg.sender).transfer(amount);
}
}

This is as opposed to the more intuitive sending pattern:


// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.4;

contract SendContract {
address payable public richest;
uint public mostSent;

/// The amount of Ether sent was not higher than


/// the currently highest amount.
error NotEnoughEther();

constructor() payable {
richest = payable(msg.sender);
mostSent = msg.value;
}

function becomeRichest() public payable {


if (msg.value <= mostSent) revert NotEnoughEther();
// This line can cause problems (explained below).
richest.transfer(msg.value);
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;

// Now follows a list of errors that


// this contract can generate together
// with a textual explanation in special
// comments.

/// Sender not authorized for this


/// operation.
error Unauthorized();

/// Function called too early.


error TooEarly();

/// Not enough Ether sent with function call.


error NotEnoughEther();

// Modifiers can be used to change


// the body of a function.
// If this modifier is used, it will
// prepend a check that only passes
// if the function is called from
// a certain address.
modifier onlyBy(address account)

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.
_;
}

/// Make `newOwner` the new owner of this


/// contract.
function changeOwner(address newOwner)
public
onlyBy(owner)
{
owner = newOwner;
}

modifier onlyAfter(uint time) {


if (block.timestamp < time)
revert TooEarly();
_;
}

/// Erase ownership information.


/// May only be called 6 weeks after
/// the contract has been created.
function disown()
public
onlyBy(owner)
onlyAfter(creationTime + 6 weeks)
{
delete owner;
}

// This modifier requires a certain


// fee being associated with a function call.
// If the caller sent too much, he or she is
// refunded, but only after the function body.
// This was dangerous before Solidity version 0.4.0,
// where it was possible to skip the part after `_;`.
modifier costs(uint amount) {
if (msg.value < amount)
revert NotEnoughEther();

_;
if (msg.value > amount)
payable(msg.sender).transfer(msg.value - amount);
}

function forceOwnerChange(address newOwner)


public
payable
costs(200 ether)
{
owner = newOwner;
// just some example condition
if (uint160(owner) & 0 == 1)
// This did not refund for Solidity
// before version 0.4.0.
return;
// refund overpaid fees
}
}

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();

// This is the current stage.


Stages public stage = Stages.AcceptingBlindedBids;

uint public creationTime = block.timestamp;

modifier atStage(Stages stage_) {


if (stage != stage_)
revert FunctionInvalidAtThisStage();
_;
}

function nextStage() internal {


stage = Stages(uint(stage) + 1);
}

// Perform timed transitions. Be sure to mention


// this modifier first, otherwise the guards
// will not take the new stage into account.
modifier timedTransitions() {
if (stage == Stages.AcceptingBlindedBids &&
block.timestamp >= creationTime + 10 days)
nextStage();
if (stage == Stages.RevealBids &&
block.timestamp >= creationTime + 12 days)
nextStage();
// The other stages transition by transaction
_;
}

// Order of the modifiers matters here!


function bid()
public
payable
timedTransitions
atStage(Stages.AcceptingBlindedBids)
{
// We will not implement that here
}

function reveal()
public
timedTransitions
atStage(Stages.RevealBids)
{
}

// This modifier goes to the next stage


// after the function is done.
modifier transitionNext()
{
_;
nextStage();
}

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

You might also like