Skip to content

Instantly share code, notes, and snippets.

@amithkk
Last active August 27, 2022 17:00
Show Gist options
  • Save amithkk/383a8a2d34361f35f794277b08340c19 to your computer and use it in GitHub Desktop.
Save amithkk/383a8a2d34361f35f794277b08340c19 to your computer and use it in GitHub Desktop.
18 Steps to Solidity Fluency - based on Solidity By Example. Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.8.14+commit.80d49f37.js&optimize=false&runs=200&gist=
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13; //
contract HelloWorld {
// Variable declaration <datatype> <visibility> <name> = <initial-value>
string public hello = "Hello World!";
}
// SPDX-License-Identifier: UNLICENSED
// Storing data on ethereum is expensive!
// SLIDES AVAILABLE
// Types of Storage
// Storage - Permanent (Like your disk)
// Memory - Only when functions are executing (Like RAM)
// Calldata - Only for function parameters - NEEDED when calling into another contract from one contract
// Types of function
// One that creates a transaction (costs)
// One that only reads (free)
pragma solidity ^0.8.13;
contract SimpleStorage {
// State variable to store a number
uint public num;
string public str;
// You need to send a transaction to write to a state variable.
// You cannot set location for a non array/mapping/string type
function set(uint _num) public {
num = _num;
}
function setString(string memory _str) public {
str = _str;
}
// You can read from a state variable without sending a transaction.
function get() public view returns (uint) {
return num;
}
// You dont need to write a getter for public variables
}
// SPDX-License-Identifier: UNLICENSED
// SLIDES AVAILABLE
pragma solidity ^0.8.13;
contract Gas {
uint public i = 0;
// Using up all of the gas that you send causes your transaction to fail.
// State changes are undone.
// Gas spent are not refunded.
// WARNING: Reduce your gas limits before running this
function forever() public {
// Here we run a loop until all of the gas are spent
// and the transaction fails
while (true) {
i += 1;
}
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
contract Function {
// Functions can return multiple values.
function returnMany()
public
pure
returns (
uint,
bool,
uint
)
{
return (1, true, 2);
}
// Return values can be named.
function named()
public
pure
returns (
uint x,
bool b,
uint y
)
{
return (1, true, 2);
}
// Return values can be assigned to their name.
// In this case the return statement can be omitted.
function assigned()
public
pure
returns (
uint x,
bool b,
uint y
)
{
x = 1;
b = true;
y = 2;
}
// Use destructuring assignment when calling another
// function that returns multiple values.
function destructuringAssignments()
public
pure
returns (
uint,
bool,
uint,
uint,
uint
)
{
(uint i, bool b, uint j) = returnMany();
// Values can be left out.
(uint x, , uint y) = (4, 5, 6);
return (i, b, j, x, y);
}
// Cannot use map for either input or output
// Can use array for input
function arrayInput(uint[] memory _arr) public {}
// Can use array for output
uint[] public arr;
function arrayOutput() public view returns (uint[] memory) {
return arr;
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
contract ViewAndPure {
uint public x = 1;
// Promise not to modify the state.
// https://docs.soliditylang.org/en/v0.8.14/contracts.html#view-functions
function addToX(uint y) public view returns (uint) {
return x + y;
}
// Promise not to modify or read from the state.
// https://docs.soliditylang.org/en/v0.8.14/contracts.html#pure-functions
function add(uint i, uint j) public pure returns (uint) {
return i + j;
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
contract FunctionModifier {
// We will use these variables to demonstrate how to use
// modifiers.
address public owner;
uint public x = 10;
bool public locked;
constructor() {
// Set the transaction sender as the owner of the contract.
owner = msg.sender;
}
// Modifier to check that the caller is the owner of
// the contract.
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
// Underscore is a special character only used inside
// a function modifier and it tells Solidity to
// execute the rest of the code.
_;
}
// Modifiers can take inputs. This modifier checks that the
// address passed in is not the zero address.
modifier validAddress(address _addr) {
require(_addr != address(0), "Not valid address");
_;
}
function changeOwner(address _newOwner) public onlyOwner validAddress(_newOwner) {
owner = _newOwner;
}
// Modifiers can be called before and / or after a function.
// This modifier prevents a function from being called while
// it is still executing.
modifier noReentrancy() {
require(!locked, "No reentrancy");
locked = true;
_;
locked = false;
}
function decrement(uint i) public noReentrancy {
x -= i;
if (i > 1) {
decrement(i - 1);
}
}
}
// SPDX-License-Identifier: UNLICENSED
// SLIDES AVAILABLE
pragma solidity ^0.8.13;
// Base contract X
contract X {
string public name;
event LogX(string message);
constructor(string memory _name) {
name = _name;
emit LogX(_name);
}
}
// Base contract Y
contract Y {
string public text;
event LogY(string message);
constructor(string memory _text) {
text = _text;
emit LogY(_text);
}
}
// There are 2 ways to initialize parent contract with parameters.
// Pass the parameters here in the inheritance list. (Compile Time)
contract B is X("Input to X"), Y("Input to Y") {
}
contract C is X, Y {
// Pass the parameters here in the constructor,
// similar to function modifiers. (Publish Time)
constructor(string memory _name, string memory _text) X(_name) Y(_text) {}
}
// Inheritance graph must be linearizable
// So no M from C,
// COMMON GOTCHA!!
// Parent constructors are always called in the order of inheritance
// regardless of the order of parent contracts listed in the
// constructor of the child contract.
// Order of constructors called:
// 1. X
// 2. Y
// 3. D
contract D is X, Y {
constructor() X("X was called") Y("Y was called") {}
}
// Order of constructors called:
// 1. X
// 2. Y
// 3. E
contract E is X, Y {
constructor() Y("Y was called") X("X was called") {}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
contract Base {
// Private function can only be called
// - inside this contract
// Contracts that inherit this contract cannot call this function.
function privateFunc() private pure returns (string memory) {
return "private function called";
}
function testPrivateFunc() public pure returns (string memory) {
return privateFunc();
}
// Internal function can be called
// - inside this contract
// - inside contracts that inherit this contract
function internalFunc() internal pure returns (string memory) {
return "internal function called";
}
function testInternalFunc() public pure virtual returns (string memory) {
return internalFunc();
}
// Public functions can be called
// - inside this contract
// - inside contracts that inherit this contract
// - by other contracts and accounts
function publicFunc() public pure returns (string memory) {
return "public function called";
}
// External functions can only be called
// - by other contracts and accounts
function externalFunc() external pure returns (string memory) {
return "external function called";
}
// This function will not compile since we're trying to call
// an external function here.
// function testExternalFunc() public pure returns (string memory) {
// return externalFunc();
// }
// State variables
string private privateVar = "my private variable";
string internal internalVar = "my internal variable";
string public publicVar = "my public variable";
// State variables cannot be external so this code won't compile.
// string external externalVar = "my external variable";
}
contract Child is Base {
// Inherited contracts do not have access to private functions
// and state variables.
// function testPrivateFunc() public pure returns (string memory) {
// return privateFunc();
// }
// Internal function call be called inside child contracts.
function testInternalFunc() public pure override returns (string memory) {
return internalFunc();
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
contract Event {
// Event declaration
// Up to 3 parameters can be indexed.
// Indexed parameters helps you filter the logs by the indexed parameter
event Log(address indexed sender, string message);
event AnotherLog();
function test() public {
emit Log(msg.sender, "Hello World!");
emit Log(msg.sender, "Hello EVM!");
emit AnotherLog();
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
contract Error {
function testRequire(uint _i) public pure {
// Require should be used to validate conditions such as:
// - inputs
// - conditions before execution
// - return values from calls to other functions
require(_i > 10, "Input must be greater than 10");
}
function testRevert(uint _i) public pure {
// Revert is useful when the condition to check is complex.
// This code does the exact same thing as the example above
if (_i <= 10) {
revert("Input must be greater than 10");
}
}
uint public num;
function testAssert() public view {
// Assert should only be used to test for internal errors,
// and to check invariants.
// Here we assert that num is always equal to 0
// since it is impossible to update the value of num
// Assert burns all gas
assert(num == 0);
}
// custom error
error InsufficientBalance(uint balance, uint withdrawAmount);
function testCustomError(uint _withdrawAmount) public view {
uint bal = address(this).balance;
if (bal < _withdrawAmount) {
revert InsufficientBalance({balance: bal, withdrawAmount: _withdrawAmount});
}
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
contract Loop {
function loop() public {
// for loop
for (uint i = 0; i < 10; i++) {
if (i == 3) {
// Skip to next iteration with continue
continue;
}
if (i == 5) {
// Exit loop with break
break;
}
}
// while loop
uint j;
while (j < 10) {
j++;
}
}
}
// SPDX-License-Identifier: UNLICENCED
pragma solidity ^0.8.13;
contract Array {
// Several ways to initialize an array
uint[] public arr;
uint[] public arr2 = [1, 2, 3];
// Fixed sized array, all elements initialize to 0
uint[10] public myFixedSizeArr;
function get(uint i) public view returns (uint) {
return arr[i];
}
// Solidity can return the entire array.
// Do NOT Do this for non view functions
function getArr() public view returns (uint[] memory) {
return arr;
}
function push(uint i) public {
// Append to array
// This will increase the array length by 1.
arr.push(i);
}
function pop() public {
// Remove last element from array
// This will decrease the array length by 1
arr.pop();
}
function getLength() public view returns (uint) {
return arr.length;
}
function remove(uint index) public {
// Delete does not change the array length.
// It resets the value at index to it's default value,
// in this case 0
delete arr[index];
}
function examples() external {
// create array in memory, only fixed size can be created
uint[] memory a = new uint[](5);
}
}
// SPDX-License-Identifier: UNLICENCED
pragma solidity ^0.8.13;
contract Mapping {
// Mapping from address to uint
mapping(address => uint) public myMap;
function get(address _addr) public view returns (uint) {
// Mapping always returns a value.
// If the value was never set, it will return the default value.
return myMap[_addr];
}
function set(address _addr, uint _i) public {
// Update the value at this address
myMap[_addr] = _i;
}
function remove(address _addr) public {
// Reset the value to the default value.
delete myMap[_addr];
}
}
contract NestedMapping {
// Nested mapping (mapping from address to another mapping)
mapping(address => mapping(uint => bool)) public nested;
function get(address _addr1, uint _i) public view returns (bool) {
// You can get values from a nested mapping
// even when it is not initialized
return nested[_addr1][_i];
}
function set(
address _addr1,
uint _i,
bool _boo
) public {
nested[_addr1][_i] = _boo;
}
function remove(address _addr1, uint _i) public {
delete nested[_addr1][_i];
}
}
// SPDX-License-Identifier: UNLICENCED
pragma solidity ^0.8.13;
contract Enum {
// Enum representing shipping status
enum Status {
Pending,
Shipped,
Accepted,
Rejected,
Canceled
}
// Default value is the first element listed in
// definition of the type, in this case "Pending"
Status public status;
// Returns uint
// Pending - 0
// Shipped - 1
// Accepted - 2
// Rejected - 3
// Canceled - 4
function get() public view returns (Status) {
return status;
}
// Update status by passing uint into input
function set(Status _status) public {
status = _status;
}
// You can update to a specific enum like this
function cancel() public {
status = Status.Canceled;
}
// delete resets the enum to its first value, 0
function reset() public {
delete status;
}
}
// SPDX-License-Identifier: UNLICENCED
pragma solidity ^0.8.13;
contract Todos {
struct Todo {
string text;
bool completed;
}
// An array of 'Todo' structs
Todo[] public todos;
function create(string calldata _text) public {
// 3 ways to initialize a struct
// - calling it like a function
todos.push(Todo(_text, false));
// key value mapping
todos.push(Todo({text: _text, completed: false}));
// initialize an empty struct and then update it
Todo memory todo;
todo.text = _text;
// todo.completed initialized to false
todos.push(todo);
}
// Solidity automatically created a getter for 'todos' so
// you don't actually need this function.
function get(uint _index) public view returns (string memory text, bool completed) {
Todo storage todo = todos[_index];
return (todo.text, todo.completed);
}
// update text
function updateText(uint _index, string calldata _text) public {
Todo storage todo = todos[_index];
todo.text = _text;
}
// update completed
function toggleCompleted(uint _index) public {
Todo storage todo = todos[_index];
todo.completed = !todo.completed;
}
}
// SPDX-License-Identifier: UNLICENCED
pragma solidity ^0.8.13;
contract Payable {
// Payable address can receive Ether
address payable public owner;
// Payable constructor can receive Ether
constructor() payable {
owner = payable(msg.sender);
}
// Function to deposit Ether into this contract.
// Call this function along with some Ether.
// The balance of this contract will be automatically updated.
function deposit() public payable {}
// Call this function along with some Ether.
// The function will throw an error since this function is not payable.
function notPayable() public {}
// Function to withdraw all Ether from this contract.
function withdraw() public {
// get the amount of Ether stored in this contract
uint amount = address(this).balance;
// send all Ether to owner
// Owner can receive Ether since the address of owner is payable
(bool success, ) = owner.call{value: amount}("");
require(success, "Failed to send Ether");
}
// Function to transfer Ether from this contract to address from input
function transfer(address payable _to, uint _amount) public {
// Note that "to" is declared as payable
(bool success, ) = _to.call{value: _amount}("");
require(success, "Failed to send Ether");
}
}
// SPDX-License-Identifier: UNLICENCED
pragma solidity ^0.8.13;
contract ReceiveEther {
/*
Which function is called, fallback() or receive()?
send Ether
|
msg.data is empty?
/ \
yes no
/ \
receive() exists? fallback()
/ \
yes no
/ \
receive() fallback()
*/
// Function to receive Ether. msg.data must be empty
receive() external payable {}
// Fallback function is called when msg.data is not empty
fallback() external payable {}
function getBalance() public view returns (uint) {
return address(this).balance;
}
}
contract SendEther {
function sendViaTransfer(address payable _to) public payable {
// This function is no longer recommended for sending Ether.
_to.transfer(msg.value);
}
function sendViaSend(address payable _to) public payable {
// Send returns a boolean value indicating success or failure.
// This function is not recommended for sending Ether.
bool sent = _to.send(msg.value);
require(sent, "Failed to send Ether");
}
function sendViaCall(address payable _to) public payable {
// Call returns a boolean value indicating success or failure.
// This is the current recommended method to use.
(bool sent, bytes memory data) = _to.call{value: msg.value}("");
require(sent, "Failed to send Ether");
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.0.0/contracts/token/ERC20/ERC20.sol";
contract AmithsToken is ERC20 {
constructor(string memory name, string memory symbol) ERC20(name, symbol) {
// Mint 100 tokens to msg.sender
// Similar to how
// 1 Rupee = 100 paise
// 1 token = 1 * (10 ** decimals)
_mint(msg.sender, 100 * 10**uint(decimals()));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment