Simple Storage | Solidity Fundamentals

Setting up first Contract
boiler plate code
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18; // stated our version
contract SimpleStorage {
}
Pointers :
// SPDX-License-Identifier: MITto let people and automated tools know the code is free to use, copy, modify, and share.pragma solidity 0.8.18;says our code needs to be exactly 0.8.18 version,pragma solidity ^0.8.18;our code can be compatible with anything 0.8.18 or above,pragma solidity >=0.8.18 <0.9.0;tells compiler that any version within the range works.contract is like a Class in other languages.
What does compiling a contract mean?
It converts the Solidity code into bytecode and ABI that can be understood and executed by the Ethereum Virtual Machine (EVM). ABI stands for Application Binary Interface. It is a standard JSON file that acts as a translator between human-readable programming and the Ethereum Virtual Machine (EVM).
Basic Variable Types
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18; // stated our version
contract SimpleStorage {
// Basic types : boolean, uint, int, address, bytes
bool gay = true;
uint256 favouriteNumber = 42;
string name = "0xgambit";
int256 credit = -250;
address myAddress = 0xF1Efb0e72A0fCf91D168790b8cD4C97614550A30;
bytes32 favouriteBytes32 = "cat";
}
Pointers :
boolean is a true / false value, uint is a un-signed integer aka a positive whole number, int is a positive or negative whole number, address will be like a wallet address
uint and int are special as we can specify how many bits we want to assign to them while creating a variable, eg : uint256 (256 bits / 32 bytes) of storage, can store [0, 2^256-1], if you do not specify how many bits then unit defaults to uint256. We may also have 8 bits, 16 bits, 56 bits, etc..
stringis essentially abytesarray wrapper, you can also specify bits for bytes likebytes32, but remember unlike uint bytes32 and bytes are different (will learn later)The default values for these types are:
bool:falseuint(all sizes likeuint8touint256):0int(all sizes likeint8toint256):0address(andaddress payable):0x0000000000000000000000000000000000000000(often referred to asaddress(0)or the zero address)bytes(dynamically-sized):0x(an empty byte array)bytes1tobytes32(fixed-size):0x00...(padded with zeros to match the specified number of bytes)
Functions
Functions execute a code block and may or may not return a value, just like other languages.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18; // stated our version
contract SimpleStorage {
uint256 public favoriteNumber;
function updateFavorite(uint256 _favoriteNumber) public{
favoriteNumber = _favoriteNumber;
}
}
We can compile and deploy the contract in remix test environment now.
if we call the favouriteNumber (doesn't cost gas as we aren't changing blockchain data) we get the value 0.
If we call the updateFavourite function with a number of our own (costs gas) it will update the variable value.
Now if we call the data we will get the value 69, as updateFavourite changed the state of blockchain.
Function Visibility Specifiers
Visibility keywords dictate where and who can call the function. You must choose one for every function:
public: Callable internally by the contract and externally by users or other contracts, it also creates a getter function for storage/state variables.external: Only callable from outside the contract. (Saves gas for large array inputs).internal(default): Only accessible inside the current contract or contracts inheriting it.private: Only accessible inside the exact contract where it is defined.
scope of variables inside or outside functions are just like other languages, follow the curly brackets {}
// getter function
function retrieve() public view returns(uint256){
return favoriteNumber;
}
We are saying that we wanna return favouriteNumber of type uint256.
State Mutability Modifiers
These keywords notify the Ethereum Virtual Machine (EVM) how the function interacts with the blockchain state, directly impacting gas fees:
Default (State-changing): Modifies state variables and requires a paid transaction fee (gas).
view: Reads data from the blockchain but cannot modify it. Free to call externally.pure: Neither reads nor modifies blockchain state. Used for isolated math or helper logic.
Arrays and Structs
Struct basics
A struct allows us to create custom data structure with multiple fields.
struct User {
uint256 id;
string name;
bool isActive;
}
// Creating and storing a struct instance
User public newUser = User(1, "Alice", true);
// Accessing fields using dot notation
string memory userName = newUser.name; // Returns "Alice"
Array basics
Arrays in solidity are collection of elements of the exact same data type (including structs).
They can be fixed(decided at compile time) or dynamic(can grow/shrink during run time).
// Example syntax of fixed size array
uint256[5] public numbers;
// Example syntax of dynamic size array
uint256[] public IDs;
Common Array Operations (Dynamic Arrays Only):
.push(element): Adds an item to the end of the array. Increases length by 1..pop(): Removes the last item from the array. Decreases length by 1..length: Returns the current number of elements in the array.
Combined Example
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18; // stated our version
contract SimpleStorage {
// custom data structure called Person
struct Person{
uint256 favouriteNumber;
string name;
}
// dynamic array of type Person
Person[] public listOfpeople;
function addPerson(string memory _name, uint256 _favouriteNumber) public {
// Person memory newPerson = Person(_favouriteNumber, _name);
// listOfpeople.push(newPerson);
listOfpeople.push(Person(_favouriteNumber, _name));
}
}
example Person added after deploying contract.
Since listOfpeople is public we can check it by proving the index number.
Memory Storage and Calldata
Key distinctions:
storagechanges are saved on the blockchain and cost gas. Astoragereference points to the actual state variable. If you make a variable outside of function inside a contract it defaults to storage variable.memoryis temporary workspace. It disappears when the transaction/call finishes. Generally used as parameter defining in functions. You usually writememoryfor temporary arrays, strings, structs, mapping, orbytes, simple values don't needmemory.calldata, read-only delivery package , holds externally supplied arguments without copying them into memory. It is generally cheaper thanmemory, but immutable. Usingcalldataavoids making an unnecessary temporary copy, so it is usually cheaper than usingmemoryfor input you only read.
Example code :
contract Notes {
string[] public savedNotes; // storage: stays on the blockchain
function save(string calldata newNote) external {
string memory editableNote = newNote;
// We can change the temporary copy if needed
// editableNote = "Updated note";
savedNotes.push(editableNote); // copied into permanent storage
}
}
When save() finishes:
editableNote(inmemory) disappears.newNote(incalldata) also disappears.savedNotes(instorage) remains permanently on the blockchain.
Mapping
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18; // stated our version
contract SimpleStorage {
// custom data structure called Person
struct Person{
uint256 favouriteNumber;
string name;
}
// dynamic array of type Person
Person[] public listOfpeople;
// Mapping, name => favouriteNumber
mapping(string => uint256) public nameToFavouriteNumber;
function addPerson(string memory _name, uint256 _favouriteNumber) public {
// Person memory newPerson = Person(_favouriteNumber, _name);
// listOfpeople.push(newPerson);
listOfpeople.push(Person(_favouriteNumber, _name));
nameToFavouriteNumber[_name] = _favouriteNumber;
}
}
If we don't give a key for a value it give the default value of it's data type.
