UNIT 3
UNIT 3
What is Solidity?
o Solidity is a contract-oriented programming language used to write smart
contracts that run on the Ethereum Virtual Machine (EVM).
o It is statically typed, and contracts are deployed on the Ethereum blockchain to
enable decentralized applications (dApps).
o Supports inheritance, libraries, and complex user-defined types.
A. Introduction
1. Pragma Directive
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
2. Import Statements
solidity
import "@openzeppelin/contracts/access/Ownable.sol";
import "./MyLibrary.sol";
3. Contract Declaration
solidity
contract MyContract is Ownable {
// Contract content
}
4. State Variables
solidity
uint256 public totalSupply;
address public owner;
mapping(address => uint256) public balances;
5. Events
solidity
event Transfer(address indexed from, address indexed to, uint256 value);
6. Modifiers
solidity
modifier onlyOwner() {
require(msg.sender == owner, "Not the owner");
_;
}
7. Constructor
solidity
constructor() {
owner = msg.sender;
}
8. Functions
Types of Functions:
1. Public & External Functions
solidity
function transfer(address to, uint256 amount) public returns (bool) {
require(balances[msg.sender] >= amount, "Insufficient balance");
balances[msg.sender] -= amount;
balances[to] += amount;
emit Transfer(msg.sender, to, amount);
return true;
}
solidity
function _mint(address account, uint256 amount) internal {
balances[account] += amount;
totalSupply += amount;
}
solidity
function getBalance(address account) public view returns (uint256) {
return balances[account];
}
solidity
receive() external payable {
// Handle Ether payments
}
Fallback Function
solidity
fallback() external payable {
// Handles non-matching function calls
}
solidity
struct User {
address account;
uint256 balance;
}
11. Mappings
solidity
.
mapping(address => uint256) public balanceOf;
12. Error Handling
solidity
error Unauthorized();
1. Introduction
Solidity is a statically typed language, meaning each variable must be explicitly declared
with a type.
It supports value types (primitives) and reference types (complex structures).
Solidity provides units for time and Ether measurements.
Solidity also has global variables that provide blockchain-related data.
solidity
bool isActive = true;
uint256 totalSupply = 1000;
int256 temperature = -20;
address owner = msg.sender;
bytes32 hash = keccak256(abi.encodePacked("Hello"));
B. Reference Types
struct Custom data structure struct Person { string name; uint age; }
solidity
struct Person {
string name;
uint age;
}
Person public user = Person("Alice", 25);
3. Units in Solidity
A. Ether Units
wei 1
solidity
uint oneEther = 1 ether; // 1 ether = 10^18 wei
uint oneGwei = 1 gwei; // 1 gwei = 10^9 wei
uint oneWei = 1 wei; // Smallest Ether unit
B. Time Units
Unit Equivalent
minutes 60 seconds
hours 60 minutes
days 24 hours
weeks 7 days
solidity
uint oneMinute = 1 minutes; // 60 seconds
uint oneHour = 1 hours; // 3600 seconds
uint oneDay = 1 days; // 86400 seconds
uint oneWeek = 1 weeks; // 604800 seconds
Solidity provides global variables that offer useful information about the blockchain
environment.
solidity
contract GlobalVars {
function getInfo() public view returns (address, uint, uint) {
return (msg.sender, block.timestamp, block.number);
}
}
1. Introduction
In Solidity, functions serve as the primary mechanism for interaction within smart contracts.
Functions can take input parameters (to receive data) and produce output parameters (to return
results).
Input parameters: Allow users to supply values when calling a function.
Output parameters: Return computed results after function execution.
Understanding how parameters work is crucial for writing efficient, secure, and gas-optimized
smart contracts.
Definition
Input parameters allow external or internal callers to pass values into a function.
They are defined within the function’s parentheses.
Solidity supports primitive data types (e.g., uint, bool, address) and complex data types
(e.g., structs, arrays, mappings).
solidity
function functionName(type param1, type param2) public returns (returnType) {
// Function logic
}
solidity
contract Calculator {
function add(uint256 a, uint256 b) public pure returns (uint256) {
return a + b;
}
}
The function add(uint256 a, uint256 b) receives two uint256 numbers as input and returns
their sum.
The pure modifier indicates that the function does not modify blockchain state.
3. Output Parameters in Solidity
Definition
solidity
function functionName(type param1, type param2) public returns (returnType) {
return value;
}
solidity
contract Greeter {
function greet() public pure returns (string memory) {
return "Hello, Solidity!";
}
}
Definition
Solidity allows a function to return more than one value, making it more efficient for batch
processing of data.
solidity
contract UserInfo {
function getUserData() public pure returns (string memory, uint256) {
return ("Alice", 25);
}
}
solidity
contract MathOperations {
function calculate(uint256 a, uint256 b) public pure returns (uint256 sum, uint256 product) {
sum = a + b;
product = a * b;
}
}
The function automatically returns sum and product without explicitly using return.
solidity
contract Test {
function getResults() public pure returns (uint256, uint256) {
return (5, 10);
}
Solidity requires explicit memory management for reference types (string, arrays,
structs).
The three data locations in Solidity are:
Data
Description Usage
Location
Temporary data storage (deleted after Used for function parameters and return
memory
execution). values.
storage Permanent storage on the blockchain. Used for contract state variables.
solidity
contract DataStorage {
string public storedData;
1. Introduction
Control structures are essential programming constructs that determine the flow of execution in
Solidity smart contracts. Solidity supports traditional control structures similar to other languages
like C++, Java, and Python. These include:
Understanding these structures is crucial for writing secure, efficient, and gas-optimized smart
contracts.
1. Conditional Statements
Conditional statements control the execution of code based on Boolean expressions. Solidity
provides:
if statement
if-else statement
else-if ladder
1.1 if Statement
solidity
if (condition) {
// Code to execute if condition is true
}
Example:
solidity
contract ConditionalExample {
function checkNumber(uint256 num) public pure returns (string memory) {
if (num > 0) {
return "Positive Number";
}
return "Zero or Negative";
}
}
1.2 if-else Statement
solidity
if (condition) {
// Executes if condition is true
} else {
// Executes if condition is false
}
Example:
solidity
contract CheckEvenOdd {
function isEven(uint256 num) public pure returns (string memory) {
if (num % 2 == 0) {
return "Even Number";
} else {
return "Odd Number";
}
}
}
solidity
if (condition1) {
// Executes if condition1 is true
} else if (condition2) {
// Executes if condition2 is true
} else {
// Executes if none of the above conditions are true
}
Example:
solidity
contract CheckGrade {
function getGrade(uint256 marks) public pure returns (string memory) {
if (marks >= 90) {
return "A+";
} else if (marks >= 80) {
return "A";
} else if (marks >= 70) {
return "B";
} else {
return "Fail";
}
}
}
2. Looping Constructs
Loops help in executing repetitive tasks without writing redundant code. Solidity supports:
1. for loop
2. while loop
3. do-while loop
solidity
.
for (initialization; condition; increment/decrement) {
// Code to execute
}
Example:
solidity
contract LoopExample {
function sumFirstN(uint256 n) public pure returns (uint256 sum) {
for (uint256 i = 1; i <= n; i++) {
sum += i;
}
}
}
✅ Gas Consideration: Avoid infinite loops, as they consume all gas and fail execution.
solidity
.
while (condition) {
// Code to execute
}
Example:
solidity
contract WhileLoopExample {
function sumUsingWhile(uint256 n) public pure returns (uint256 sum) {
uint256 i = 1;
while (i <= n) {
sum += i;
i++;
}
}
}
✅ Gas Optimization: Ensure loops terminate within a limited number of iterations to prevent
excessive gas consumption.
solidity
do {
// Code to execute
} while (condition);
Example:
solidity
contract DoWhileExample {
function countDown(uint256 n) public pure returns (uint256 count) {
do {
count++;
n--;
} while (n > 0);
}
}
✅ Use Case: When execution must happen at least once, regardless of the condition.
3. Break Statement
� Definition:
The break statement terminates the loop immediately and exits, regardless of whether the loop
condition is still true.
Control is transferred to the first statement after the loop.
� Syntax:
solidity
CopyEdit
for (initialization; condition; increment/decrement) {
if (condition_to_break) {
break; // Exit the loop immediately
}
// Code to execute if condition_to_break is false
}
� Example:
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract BreakExample {
uint[] public data;
� Explanation:
The loop iterates from 0 to 9.
If i == _n, the break statement stops the loop.
Any value of i before _n is added to the data array.
For example, if _n = 5, the data array will be [0, 1, 2, 3, 4] and the loop stops.
4. Continue Statement
� Definition:
The continue statement skips the current iteration and proceeds to the next iteration of the loop.
The remaining code inside the loop for that iteration is skipped.
� Syntax:
solidity
for (initialization; condition; increment/decrement) {
if (condition_to_skip) {
continue; // Skip the current iteration and proceed to the next one
}
// Code to execute if condition_to_skip is false
}
� Example:
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ContinueExample {
uint[] public data;
� Explanation:
The loop iterates from 0 to _n - 1.
continue skips the current iteration if i is even (i % 2 == 0).
Only odd numbers are pushed to the data array.
For _n = 10, the data array will contain [1, 3, 5, 7, 9].
Scope Stops executing all loop iterations Skips part of the loop for that iteration
Usage When you want to exit a loop early When you want to skip some iterations
1. Introduction
Solidity allows smart contracts to interact with each other through function calls and enables the
creation of new contracts using the new keyword. This is crucial for building modular, reusable,
and upgradeable smart contract architectures.
Functions in Solidity can be internal, external, public, or private, and can be invoked differently
based on their visibility and caller type.
✅ Can only be called within the same contract or from derived contracts (via inheritance).
✅ Cheaper gas cost because they do not require external transactions.
Example:
solidity
contract Parent {
function internalFunction() internal pure returns (string memory) {
return "Internal Function Called!";
}
}
solidity
contract ContractA {
function getValue() public pure returns (string memory) {
return "Hello from ContractA";
}
}
contract ContractB {
function callContractA(address contractAAddress) public view returns (string memory) {
ContractA contractA = ContractA(contractAAddress);
return contractA.getValue();
}
}
Solidity provides low-level function calls, but they should be used with caution due to security
risks.
call() Calls another contract’s function but with less type safety.
solidity
contract ExternalContract {
function externalFunction() public pure returns (string memory) {
return "Hello from External Contract!";
}
}
contract Caller {
function callExternalFunction(address externalContract) public returns (bool, bytes memory) {
(bool success, bytes memory data) =
externalContract.call(abi.encodeWithSignature("externalFunction()"));
return (success, data);
}
}
1.1 Introduction
In Solidity, contracts can dynamically create new instances of other contracts using the new
keyword. This is useful for factory patterns, where one contract deploys multiple instances of
another contract.
1.2 Syntax for Creating Contracts
solidity
ContractName contractInstance = new ContractName(constructorArguments);
solidity
contract Child {
string public message;
contract Factory {
Child public deployedChild;
A factory contract can create multiple instances of a contract and manage them efficiently.
solidity
.
contract Product {
string public name;
✅ The ProductFactory contract creates and tracks multiple Product contract instances.
1. Introduction
Solidity follows a hierarchical order when evaluating expressions. Operators with higher
precedence are evaluated first, followed by those with lower precedence.
` ` Bitwise OR
` `
solidity
uint result = 10 + 5 * 2;
✅ Evaluation Order:
solidity
.
uint result = (10 + 5) * 2; // Evaluates to 30
Example:
solidity
uint a = 2 ** 3 ** 2; // 2 ** (3 ** 2) = 2 ** 9 = 512
Solidity does not guarantee strict left-to-right execution of expressions involving function calls
or storage reads/writes.
solidity
.
contract Test {
function first() public pure returns (uint) {
return 1;
}
✅ The function first() or second() may execute in any order, leading to non-deterministic
behavior in certain cases.
Example:
solidity
contract ShortCircuit {
function check(uint x) public pure returns (string memory) {
if (x > 10 && expensiveFunction()) {
return "Condition met";
}
return "Condition not met";
}
solidity
.
uint a = 10; // a is assigned the value 10
solidity
uint a = 5;
a += 3; // Equivalent to a = a + 3; (a = 8)
a *= 2; // Equivalent to a = a * 2; (a = 16)
✅ Gas Optimization: Compound assignments use less gas than separate operations.
Assignment operators have right-to-left associativity, meaning they evaluate rightmost first.
Example:
solidity
uint a;
uint b;
uint c;
a = b = c = 10; // c is assigned first, then b, then a
1. Introduction
Scoping and declarations in Solidity define how variables and functions are accessed within a
contract. Understanding scope is essential for security, gas efficiency, and avoiding unintended
state modifications.
Example:
solidity
contract Example {
uint public stateVar = 100; // State variable (stored on-chain)
}
Example:
solidity
contract Example {
function addNumbers() public pure returns (uint) {
uint localVar = 10; // Local variable (temporary)
return localVar + 5;
}
}
Example:
solidity
contract Example {
function getSender() public view returns (address) {
return msg.sender; // Returns the caller's address
}
}
solidity
contract VisibilityExample {
uint public publicVar = 10; // Accessible everywhere
uint private privateVar = 20; // Only inside this contract
uint internal internalVar = 30; // Accessible in derived contracts
✅ Best Practice:
Use internal instead of public or private for functions that should be accessible in derived
contracts.
Avoid public for sensitive data.
Solidity follows block-level scoping, meaning variables declared inside {} are not
accessible outside.
Example:
solidity
contract BlockScope {
function example() public pure returns (uint) {
if (true) {
uint x = 10; // x exists only inside this block
}
// return x; // ✅ Compilation error (x is out of scope)
return 0;
}
}
Shadowing occurs when a local variable has the same name as a state variable.
Solidity allows shadowing inside functions but disallows it in derived contracts.
solidity
contract ShadowingExample {
uint x = 50; // State variable
solidity
contract Parent {
uint public value = 100;
}
1.1. Introduction
Error handling in Solidity is crucial for ensuring contract security, preventing invalid operations,
and saving gas costs. Solidity provides three primary mechanisms for error handling:
solidity
contract RequireExample {
function setAge(uint _age) public pure returns (string memory) {
require(_age >= 18, "Age must be 18 or older");
return "You are eligible";
}
}
✅ If _age < 18, the transaction reverts with "Age must be 18 or older".
solidity
contract Payment {
function deposit() public payable {
require(msg.value > 0, "Deposit amount must be greater than zero");
}
}
✅ Gas Consideration:
solidity
contract AssertExample {
uint public totalSupply = 100;
solidity
contract RevertExample {
function withdraw(uint _amount, uint balance) public pure returns (string memory) {
if (_amount > balance) {
revert("Insufficient balance");
}
return "Withdrawal successful";
}
}