Introduction
Ethereum is a decentralized, open-source blockchain with smart contract functionality. Ether is the native cryptocurrency of the platform. After Bitcoin, it is the largest cryptocurrency by market capitalization.Ethereum has its own programming language, called Solidity. As a blockchain network, Ethereum is a decentralized public ledger for verifying and recording transactions.
Basics of Solidity and Ethereum
Variables and Math operations
Solidity is a statically typed language, which means that variable's datatype must be specified upon declaration.
There are 3 types of variables in Solidity
- State Variables : values permanently stored in contract storage
- Local Variables : values stored in memory, impermanently. Declared inside a function
- Global Variables : special variables which exists in global namespace
Global Variables in Solidity
blockhash(uint blockNumber) returns (bytes32) : Hash of the given block - only works for 256 most recent, excluding current, blocks
block.coinbase (address payable): Current block miner's address
block.difficulty (uint): Current block difficulty
block.gaslimit (uint): Current block gaslimit
block.number (uint): Current block number
block.timestamp (uint): Current block timestamp as seconds since unix epoch
gasleft() returns (uint256): Remaining gas
msg.data (bytes calldata): Complete calldata
msg.sender (address payable): Sender of the message (current caller)
msg.sig (bytes4): First four bytes of the calldata (function identifier)
msg.value (uint): Number of wei sent with the message
now (uint): Current block timestamp
tx.gasprice (uint): Gas price of the transaction
tx.origin (address payable): Sender of the transaction
Storage vs Memory
- State variables and Local Variables of structs, array are always stored in storage by default.
- Function arguments are in memory.
- Whenever a new instance of an array is created using the keyword ‘memory’, a new copy of that variable is created. Changing the array value of the new instance does not affect the original array.
Storing time in solidity
Solidity provides some native units for dealing with time.
The variable now
will return the current unix timestamp of the latest block (the number of seconds that have passed since January 1st 1970).
Solidity also contains the time units seconds
, minutes
, hours
, days
, weeks
and years
. These will convert to a uint
of the number of seconds in that length of time. So 1 minutes
is 60
, 1 hours
is 3600
(60 seconds x 60 minutes), 1 days
is 86400
(24 hours x 60 minutes x 60 seconds), etc.
Here's an example of how these time units can be useful:
Variable conversions in solidity
Exercise:
What will be the value in the numbers array after this code has run once?
Write a function for generating random number in solidity using global variables
Data Structures
Structs
Array
dynamic and fixed, public array gets their own getter functions
Array of structs
add to array
uint[] public numbers;
numbers.push(12)
getting length of an array
uint length_of_array = numbers.push(99)
Mappings
key-value store for storing and retrieving data
When to use an array and when to use a mapping ?
Use an array to iterate over objects and use a mapping for quick lookups.
Addresses in Ethereum
The Ethereum blockchain is made up of accounts, which you can think of like bank accounts. An account has a balance of Ether (the currency used on the Ethereum blockchain), and you can send and receive Ether payments to other accounts, just like your bank account can wire transfer money to other bank accounts.
Each account has an address, which you can think of like a bank account number. It's a unique identifier that points to that account, and it looks like this:
retrieving address inside a function using a global variable msg.sender
Functions
public and private
Internal and External
internal is the same as private, except that it's also accessible to contracts that inherit from this contract.
external is similar to public, except that these functions can ONLY be called outside the contract — they can't be called by other functions inside that contract.
Pure and View
Pass arguments by value vs passing reference
Constructors
Special function that has the same name as the contract. It will get executed only one time, when the contract is first created.
Function Modifiers:
Modifiers are kind of half-functions that are used to modify other functions, usually to check some requirements prior to execution.
<modifier example>
Working with Events
event Transfer(address indexed from, address indexed to, uint256 value);
Q What does the "indexed" keyword do in the below line of code? I'm guessing it just tells the event object that the following input should be logged?
The indexed parameters for logged events will allow you to search for these events using the indexed parameters as filters.
Q Can we use it other places ie outside of events?
The indexed keyword is only relevant to logged events.
Require in solidity
Revert in solidity
Working with web3.js
Exercise : Write a function to compare two strings in solidity.
function sayHiToVitalik(string memory _name) public returns (string memory) {
// Compares if _name equals "Vitalik". Throws an error and exits if not true.
// (Side note: Solidity doesn't have native string comparison, so we
// compare their keccak256 hashes to see if the strings are equal)
require(keccak256(abi.encodePacked(_name)) == keccak256(abi.encodePacked("Vitalik")));
// If it's true, proceed with the function:
return "Hi!";
}
Contracts
contract Groot {function catchphrase() public returns (string memory) {return "I am groot";}}contract BabyGroot is Doge {function anotherCatchphrase() public returns (string memory) {return "I am smol groot";}}
import "./Groot.sol";contract BabyGroot is Groot {...}
Interacting with another contracts by declaring an interface
// sample contract luckynumber.solcontract LuckyNumber {mapping(address => uint) numbers;function setNum(uint _num) public {numbers[msg.sender] = _num;}function getNum(address _myAddress) public view returns (uint) {return numbers[_myAddress];}}// declaring inheritance in another contract MyContract.solcontract NumberInterface {function getNum(address _myAddress) public view returns (uint);}// using the above declared interfacecontract MyContract {address NumberInterfaceAddress = 0xab38...// ^ The address of the FavoriteNumber contract on EthereumNumberInterface numberContract = NumberInterface(NumberInterfaceAddress);// Now `numberContract` is pointing to the other contractfunction someFunction() public {// Now we can call `getNum` from that contract:uint num = numberContract.getNum(msg.sender);// ...and do something with `num` here}}
Gas optimization tips
Struct Packing to save gas
uint defaults to uint256 , and bool is uint8 in solidity
so the above struct occupies 4 storage slots of size 256bits each.
struct options{ uint128 amount; uint64 price; uint64 expiry; bool exercised;}
After some modifications we are able to pack the amount and price variable as well as the expiry and exercised variable together. So that the options struct now occupies only 2 storage slots.
Further optimization is possible
struct options{ uint64 amount; uint56 price; // <----------- uint64 expiry; bool exercised;}
Security Best Practices
public
and external
functions, and try to think of ways users might abuse them. Remember — unless these functions have a modifier like onlyOwner
, any user can call them and pass them any data they want to.Solidity Gotchas
compile learnings from interview questions etc
underflow and overflow in solidity
Source
https://docs.soliditylang.org/en/v0.5.5/structure-of-a-contract.html#:~:text=State%20variables%20are%20variables%20whose,State%20variable%20%2F%2F%20...%20%7D
https://www.tutorialspoint.com/solidity/solidity_types.htm
https://www.bitdegree.org/learn/solidity-types
https://www.geeksforgeeks.org/solidity-types/