Functions_Solidity
Functions_Solidity
BLOCKCHAIN
CC4167
F U N C T I O N S
A function is basically a group of code that can be reused anywhere in the program, which generally
saves the excessive use of memory and decreases the runtime of the program. Creating a function
reduces the need of writing the same code over and over again. With the help of functions, a
program can be divided into many small pieces of code for better understanding and managing.
Declaring a Function
In Solidity a function is generally defined by using the function keyword, followed by the name of
the function which is unique and does not match with any of the reserved keywords. A function can
also have a list of parameters containing the name and data type of the parameter. The return
value of a function is optional but in solidity, the return type of the function is defined at the time of
declaration.
The following statements if present in the function will modify the state of the
function and the compiler will throw a warning.
•The modifiers can be used when there is a need to verify the condition automatically before
executing a particular function.
•If the given condition is not satisfied, then the function will not get executed.
Function modifiers are created by defining them and invoking the same in the required
function.
modifier modifier_name
{
// action to be taken
}
There are two variations of a function modifier:
modifier isAdmin
{
require(msg.sender == admin);
_;
}
The _; symbol is known as Merge Wildcard and this is replaced by the function
definition during execution.
•In other words, after this wildcard has been used, the control is moved to the location
where the appropriate function definition is located.
•This symbol is mandatory for all modifiers.
•The modifier may contain this wildcard anywhere.
•When the wildcard is placed at the end of the modifier, the condition is verified and
the appropriate function is executed if it is satisfied.
•When it is placed at the beginning, the appropriate function is executed first followed
by the condition verification.
pragma solidity >=0.4.22 <0.7.0;
contract modifierWithoutArg {
address admin;
struct employee
{
uint emp_id;
string emp_name;
uint age;
}
constructor() public
{
admin = msg.sender;
}
modifier isAdmin
{
require(admin == msg.sender);
_;
}
employee e;
function enterDetails (uint _empid, string memory _empname,
uint _empage) public isAdmin {
e.emp_id = _empid;
e.emp_name = _empname;
e.age = _empage;
}
}
pragma solidity >=0.4.22 <0.7.0;
contract modifierWithArg {
struct employee
{
uint emp_id;
string emp_name;
uint age;
}
Multiple modifiers may be present in a function, and each of these conditions must be met
in order for the function to be successfully executed.
To verify whether only the administrator with 5 years of experience is editing, two
modifiers namely isAdmin and isExperienced are introduced. The function enterDetails will
execute only if the user has administrator authorization and has a minimum of 5 years of
experience.
Syntax:
constructor() public
{
admin = msg.sender;
}
modifier isAdmin
{
require(admin == msg.sender);
_;
}
modifier isExperienced(uint exp)
{
if(exp>=5)
_;
else
revert("Must have a minimum of 5 years of
experience");
}
employee e;
function enterDetails (uint _empid, string memory _empname,
uint _empage) public isAdmin
isExperienced(7) {
e.emp_id = _empid;
e.emp_name = _empname;
e.age = _empage;
}
}
Modifier Overriding
Syntax:
contract base{
modifier overridingmod virtual{
}
}
contract derived is base {
modifier overridingmod override{
}
}
pragma solidity >=0.4.22 <0.7.0;
contract modifierOverride {
modifier isExperienced(uint exp) virtual {
if(exp >= 5)
_;
else
revert("Must have minimum 5 years of experience");
}
}
contract modifierdest is modifierOverride {
struct employee
{
uint emp_id;
string emp_name;
uint age;
}
employee e;
modifier isExperienced(uint exp) override {
if(exp >= 5)
_;
else
revert("Must have minimum 5 years of experience");
}
When an enum variable is declared globally, its value can be verified using a
modifier.
Syntax:
employee e;
function enterDetails (uint _eno, string memory _ename,
Status _s) public isValid(_s) {
e.emp_id=_eno;
e.emp_name=_ename;
e.s=_s;
}
}
Modifier code examples
1. Data Validation: In this example, the input data is validated based on its type.
2. Refund Ether send by accident: For every transaction made in the blockchain, some
amount of Ether needs to be paid. Also, a facility is there for transferring some amount of Ether
to other users. Sometimes, accidentally few transactions may take place.
modifier refundEther {
if(msg.value >0) throw;
_;
}
3. Charge a fee: Modifiers can also be used for verifying whether the user has paid the required
fees as shown below:
4. Send the change back: Suppose a user wants to send back the extra amount paid by a
person, modifiers can be applied to it. The implementation for the same is given below:
If the statements which modify state variables, emitting events, creating other contracts,
using selfdestruct method, transferring ethers via calls, Calling a function which is not
‘view or pure’, using low-level calls, etc are present in view functions then the compiler
throw a warning in such cases. By default, a get method is view function.
pragma solidity ^0.5.0;
contract Test {
uint num1 = 2;
uint num2 = 4;
function getResult(
) public view returns(
uint product, uint sum){
product = num1 * num2;
sum = num1 + num2;
}
}
The pure functions do not read or modify the state variables, which returns the values only
using the parameters passed to the function or local variables present in it.
If the statements which read the state variables, access the address or balance, access any
global variable block or msg, call a function that is not pure, etc are present in pure functions
then the compiler throws a warning in such cases.
pragma solidity ^0.5.0;
contract Test {
function getResult(
) public pure returns(
uint product, uint
sum){
uint num1 = 2;
uint num2 = 4;
product = num1 *
num2;
sum = num1 + num2;
}
}
Solidity – Fall Back Function
The solidity fallback function is executed if none of the other functions match the function
identifier or no data was provided with the function call.
Only one unnamed function can be assigned to a contract and it is executed whenever the
contract receives plain Ether without any data.
To receive Ether and add it to the total balance of the contract, the fallback function must be
marked payable.
If no such function exists, the contract cannot receive Ether through regular
transactions and will throw an exception.
.
Properties of a fallback function:
2.If it is not marked payable, the contract will throw an exception if it receives plain
ether without data.
5.It is also executed if the caller meant to call a function that is not available or receive()
does not exist or msg.data is not empty.
7.It is limited to 2300 gas when called by another function by using transfer() or send()
method . It is so for as to make this function call as cheap as possible
pragma solidity >=0.8.0 <0.9.0;
contract Solid_Fallback
{
string public calledFallbackFun;
contract Sender
{
function transferEther() public payable
{
(bool sent, ) =
payable(0xD4Fc541236927E2EAf8F27606bD7309C1Fc2cbee).call{value: 2 ether}("Transaction
Completed!");
require(sent, "Transaction Failed!");
}
Function overloading in Solidity lets you specify numerous functions with the same name but
varying argument types and numbers.
Solidity searches for a function with the same name and parameter types when you call a
function with certain parameters.
Function overloading lets you construct a collection of functions that accomplish similar
operations but utilize various data types.
It simplifies coding.
Function overloading may complicate your code, particularly if you write several overloaded
functions with multiple arguments.
pragma solidity ^0.8.0;
contract OverloadingExample {
function add(uint256 a, uint256 b) public pure returns
(uint256)
{
return a + b;
}
They firmly contain data as state variables and functions which can modify these
variables. When a function is called on a different instance (contract), the EVM
function call happens and the context is switched in such a way that the state
variables are inaccessible.
A contract or its function needs to be called for anything to happen. Some basic
properties of contracts are as follows :
•State Variables: These are the variables that are used to store the state of the
contract.
Creating contracts programmatically is generally done by using JavaScript API web3.js, which has
a built-in function web3.eth.Contract to create the contracts.
When a contract is created its constructor is executed, a constructor is an optional special method
defined by using the constructor keyword which executes one per contract.
Once the constructor is called the final code of the contract is added to the blockchain.
Solidity provides four types of visibilities for functions and state variables. Functions have to
specified by any of the four visibilities but for state variables external is not allowed.
1.External: External functions are can be called by other contracts via transactions. An external
function cannot be called internally. For calling an external function within the
contract this.function_name() method is used. Sometimes external functions are more efficient
when they have large arrays of data.
2.Public: Public functions or variables can be called both externally or internally via messages. For
public static variables, a getter method is created automatically in solidity.
3.Internal: These functions or variables can be accessed only internally i.e. within the contract or
the derived contracts.
4.Private: These functions or variables can only be visible for the contracts in which they are
defined. They are not accessible to derived contracts also.
pragma solidity >=0.8.6 <0.9.0;
abstract contract contract_example { function getStr(
) public virtual returns (string memory);
uint private num1; }
uint public num2;
string internal str; contract derived_contract is contract_example{
constructor(){
num2 = 10; function setStr(
} string memory _str) public override{
str = _str;
function increment( }
uint data1) private pure returns(
uint) { function getStr(
return data1 + 1; ) public view override returns (
} string memory){
return str;
function updateValue( }
uint data1) public { }
num1 = data1;
} contract D {
function readData(
function getValue( ) public returns(
) public view returns( string memory, uint) {
uint) { contract_example c = new derived_contract();
return num1; c.setStr(“Manipal University");
} c.updateValue(16);
return (c.getStr(), c.getValue());
function setStr( }
string memory _str) public virtual; }
Inheritance
is one of the most important features of the object-oriented programming language. It is a way of
extending the functionality of a program, used to separate the code, reduces the dependency,
and increases the re-usability of the existing code.
Solidity supports inheritance between smart contracts, where multiple contracts can be inherited
into a single contract.
The contract from which other contracts inherit features is known as a base contract, while the
contract which inherits the features is called a derived contract.
The scope of inheritance in Solidity is limited to public and internal modifiers only.
Some of the key highlights of Solidity are:
•A derived contract can access all non-private members including state variables and
internal methods. But using this is not allowed.
•Function overriding is allowed provided function signature remains the same. In case
of the difference of output parameters, the compilation will fail.
•We can call a super contract’s function using a super keyword or using a super
contract name.
•In the case of multiple inheritances, function calls using super gives preference to
most derived contracts.
pragma solidity >=0.4.22 <0.6.0;
contract parent{
1. Single Inheritance uint internal sum;
function getValue(
) external view returns(uint) {
return sum;
}
}
contract caller {
function testInheritance(
) public returns (uint) {
cc.setValue();
return cc.getValue();
}
}
pragma solidity >=0.4.22 <0.6.0;
2. Multi-level Inheritance
It is very similar to single inheritance, but the contract A {
difference is that it has levels of the relationship
between the parent and the child. The child string internal x;
contract derived from a parent also acts as a string a = “Life" ;
parent for the contract which is derived from it. string b = "For";
contract C is A {
function getAValue(
) external view returns(uint){
return sum;
}
}
pragma solidity >=0.4.22 <0.6.0;
function getPow(
) external returns(uint) {
return pow;
}
}
Solidity – Constructors
A constructor is a special method in any object-oriented programming language which gets
called whenever an object of a class is initialized.
It is totally different in case of Solidity, Solidity provides a constructor declaration inside the
smart contract and it invokes only once when the contract is deployed and is used to initialize
the contract state.
Creating a constructor
A Constructor is defined using a constructor keyword without any function name followed by
an access modifier.
contract constructorExample
{
string str;
constructor() public
{
str = “I am
Indian";
}
function getValue(
) public view returns (
string memory) {
return str;
}
}
Constructor in Inheritance
In case if a constructor is not defined then the default constructor is called, but if the
constructor is defined in a parent contract and having some arguments then the child
contract should also provide the required parameters to the constructor.
If the child contract is not passing any parameter to the parent’s constructor the child
contract will become an abstract contract.
1. Direct Initialization: In the below example, the direct initialization method is used to
initialize the constructor of the parent class.
pragma solidity ^0.5.0;
contract Base {
uint data;
constructor(uint _data) public {
data = _data;
}
function Print(
) public returns(string memory){
return "Direct Initialization";
}
}
contract Derived is Base(2){
constructor() public {}
function getData(
) external returns(uint){
uint result = data ** 2;
return result;
}
}
contract caller{
Derived c = new Derived();
function getResult() public returns(uint){
c.Print();
return c.getData();
}
}
pragma solidity ^0.5.0;
2. Indirect Initialization: In the below example, contract Base {
the indirect initialization string str;
constructor(
string memory _str) public {
using Base(string(abi.encodePacked(_info, _info))) str = _str;
}
is done to initialize the constructor of the base function Print(
class. ) public returns(string memory){
return "Indirect Initialization";
}
}
function getStr(
) public view returns(string memory){
return str;
}
}
contract caller {
Derived c
= new Derived(“Jaipur");
Abstract contracts are contracts that have at least one function without its implementation
or in the case when you don’t provide arguments for all of the base contract constructors.
Also in the case when we don’t intend to create a contract directly we can consider the
contract to be abstract.
Abstract contracts are used as base contracts so that the child contract can inherit and utilize
its functions.
The abstract contract defines the structure of the contract and any derived contract inherited
from it should provide an implementation for the incomplete functions, and if the derived
contract is also not implementing the incomplete functions then that derived contract will also
be marked as abstract.
function getValues(
) public returns (string memory,uint) function getStr(
{ string memory _strIn) public pure override returns(
abs.setValue(10, 16); string memory){
return (abs.getStr(“MUJ return _strIn;
}
Jaipur"),abs.add());
}
function setValue(
} uint _in1, uint _in2) public override{
num1 = _in1;
num2 = _in2;
}
function add() public view override returns(uint){
return (num2 + num1);
}
}
Deploy the Smart Contract: After successful compilation of the smart contract
select the Call contract before deploying it.
Solidity – Basics of Interface
Interfaces are the same as abstract contracts created by using an interface keyword, also known
as a pure abstract contract.
Interfaces do not have any definition or any state variables, constructors, or any function with
implementation, they only contain function declarations i.e. functions in interfaces do not have
any statements.
They can inherit from other interfaces, but they can’t inherit from other contracts.
An interface can have enum, structs which can be accessed using interface name dot notation.
pragma solidity >=0.8.6 <0.9.0;
interface InterfaceExample{
function getStr(
) external view returns(string memory);
function add(
) external view returns(uint);
}
pragma solidity >=0.8.6 <0.9.0;
contract call{
function add(
) public view virtual override returns(uint){
return num1 + num2;
}
}
Libraries in solidity are similar to contracts that contain reusable codes. A library has
functions that can be called by other contracts.
Deploying a common code by creating a library reduces the gas cost. Functions of the library
can be called directly when they do not modify the state variables i.e. only pure and view
functions can be called from outside of the library. It cannot be destroyed because it is
assumed as stateless.
The library does not have state variables, it cannot inherit any element and cannot be
inherited.
Creating Library
A library contract is defined by using the library keyword instead of a general contract.
Libraries do not have any storage thus it cannot hold state variables, fallback or payable
functions also cannot be created inside the library as it cannot store ethers.
Libraries are not for changing the state of the contract, it can only be used for performing
basic operations based on inputs and outputs.
However, it can implement some data types like struct and enums which are user-defined,
and constant variables that are stored in a stack of Ethereum, not in storage.
pragma solidity ^0.5.0;
library libraryExample {
struct Constants {
uint Pi; uint EulerNb;
uint PythagoraConst;
uint TheodorusConst; }
}
Deploying Library Using ‘For’ Keyword
Example:
A single file can contain multiple libraries that can be specified using
curly braces in the import statement separated by a comma. A library
can be accessed within the smart contract by using ‘for’ keyword.
Syntax:
<libraryName> for <dataType>
library LibExample {
function pow(
uint a, uint b) public view returns (
uint, address) {
return (a ** b, address(this));
}
}
contract LibraryExample {
function getPow(
uint num1, uint num2) public view returns (
uint, address) {
return num1.pow(num2);
}
}
pragma solidity ^0.5.0;
contract libExample{
libraryExample.strings data
= libraryExample.strings("Manipal", "University", "Jaipur
function getResult(
) public view returns(string memory){
string memory result
= libraryExample.concatenate(data.str1, data.str2);
result = libraryExample.concatenate(result, data.str3);
return result;
}
}
Inbuilt libraries
Solidity has some inbuilt libraries for the ease of the users. Some of the libraries are listed
below :
1.Modular network: This includes many modular libraries that are very useful for
implementation like ArrayUtils, Token, CrowdSale, Vesting, StringUtils, LinkedList, Wallet, etc.
2.OpenZeppelin: other supporting libraries are Roles, MerkleProof, ECDSA, Math, Address,
SafeERC20, ERC165Checker, SafeMath, Arrays, etc which protects from overflow.
An event is an inheritable member of the contract, which stores the arguments passed in the
transaction logs when emitted.
Generally, events are used to inform the calling application about the current state of the
contract, with the help of the logging facility of EVM.
Events notify the applications about the change made to the contracts and applications
which can be used to execute the dependent logic.
Creating an event
Events are defined within the contracts as global and called within their functions.
Events are declared by using the event keyword, followed by an identifier and the
parameter list, and ends with a semicolon.
The parameter values are used to log the information or for executing the conditional
logic. Its information and values are saved as part of the transactions inside the block.
There is no need of providing variables, only datatypes are sufficient. An event can be
called from any method by using its name and passing the required parameters.
pragma solidity ^0.4.21;
contract eventExample {
logs [
{
"from": "0xd9145CCE52D386f254917e481eB44e9943F39138",
"topic":
"0xfc3a67c9f0b5967ae4041ed898b05ec1fa49d2a3c22336247201d71be6f97120",
"event": "Increment",
"args": {
"0": "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4",
"owner": "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"
}
}
]
IMP: We can add atmost 3 indexes in one
event.
Index in events: We can also add an index to our event. On adding the different fields to our
event, we can add an index to them it helps to access them later but of course, it’s going to
cost some more gas!
Output in the console: adding the output in
the console
pragma solidity >=0.7.0
logs [
<0.9.0;
{
"from": "0xcD6a42782d230D7c13A74ddec5dD140e55499Df9",
"topic": contract IndexEvents {
"0xa6b5ddd331f9dc412a8c258207b1c66f53c1740c72628d9913aafcb6b28d
8f73", event NewTrade(
"event": "NewTrade", uint256 indexed date,
"args": { address from,
"0": "1655406115", address indexed to,
"1": "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4", uint256 indexed
"2": "0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2", amount
"3": "1234", );
"date": "1655406115",
"from": function trade(address
to, uint256 amount) external
"0x5B38Da6a701c568545dCfcB03FcB875f56beddC4",
{
"to": "0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2",
emit
"amount": "1234" NewTrade(block.timestamp,
} msg.sender, to,amount);
} }
] }
Solidity – Error Handling
Solidity is compiled to byte code and there a syntax error check happens at compile-time, while
runtime errors are difficult to catch and occurs mainly while executing the contracts.
Some of the runtime errors are out-of-gas error, data type overflow error, divide by zero error,
array-out-of-index error, etc.
Until version 4.10 a single throw statement was there in solidity to handle errors, so to handle
errors multiple if…else statements, one has to implement for checking the values and throw
errors which consume more gas.
After version 4.10 new error handling construct assert, require, revert statements were
introduced and the throw was made absolute.
Require Statements
The ‘require’ statements declare prerequisites for running the function i.e. it declares the
constraints which should be satisfied before executing the code.
It accepts a single argument and returns a boolean value after evaluation, it also has a custom
string message option. If false then exception is raised and execution is terminated.
The unused gas is returned back to the caller and the state is reversed to its original state.
Following are some cases when require type of exception is triggered :
•When a contract is created using the new keyword and the process does not end properly.
•When ethers are sent to the contract using the public getter method.
contract requireStatement {
Its syntax is similar to the require statement. It returns a boolean value after the evaluation of
the condition.
Based on the return value either the program will continue its execution or it will throw an
exception.
Instead of returning the unused gas, the assert statement consumes the entire gas supply and
the state is then reversed to the original state.
Assert is used to check the current state and function conditions before the execution of the
contract.
contract assertStatement {
bool result;
This statement is similar to the require statement. It does not evaluate any condition and does
not depends on any state or statement.
It is used to generate exceptions, display errors, and revert the function call.
This statement contains a string message which indicates the issue related to the information of
the exception.
Calling a revert statement implies an exception is thrown, the unused gas is returned and the
state reverts to its original state.
Revert is used to handle the same exception types as require handles, but with little bit more
complex logic.
pragma solidity ^0.5.0;
contract revertStatement {
}
Custom Error
To define the custom error in the smart contract by using the “Error statement” .
Error statement is used either inside the smart contract or outside the smart contract along
with revert statement.
The main purpose of using custom error in the smart contract is to save gas.
pragma solidity >=0.4.22 <0.9.0;
contract CustomError {
mapping(address => uint) public bal;
function deposit() public payable {
bal[msg.sender] += msg.value;
}
error LackOfFunds(uint withdrawAmt, uint availableAmt);