From 41e41ada84d1a8a3415220bec9d7063fb204c888 Mon Sep 17 00:00:00 2001 From: Ishan Lakhwani Date: Sat, 18 Jan 2025 02:23:47 +0530 Subject: [PATCH] solidity basics notes added --- src/basics/AbiDecode.sol | 107 ++++++++++++ src/basics/AbiEncode.sol | 123 ++++++++++++++ src/basics/Array.sol | 74 +++++++++ src/basics/CallExample.sol | 140 ++++++++++++++++ src/basics/Constants.sol | 8 + src/basics/ConstructorExample.sol | 108 ++++++++++++ src/basics/Counter.sol | 22 +++ src/basics/DataLocations.sol | 97 +++++++++++ src/basics/DelegateCall.sol | 120 ++++++++++++++ src/basics/Enum.sol | 68 ++++++++ src/basics/Error.sol | 116 +++++++++++++ src/basics/EtherUnits.sol | 37 +++++ src/basics/EventExample.sol | 86 ++++++++++ src/basics/Fallback.sol | 109 ++++++++++++ src/basics/ForWhile.sol | 49 ++++++ src/basics/FunctionModifier.sol | 130 +++++++++++++++ src/basics/Gas.sol | 54 ++++++ src/basics/HashFunction.sol | 97 +++++++++++ src/basics/HelloWorld.sol | 7 + src/basics/IfElse.sol | 66 ++++++++ src/basics/Immutable.sol | 12 ++ src/basics/Interface.sol | 106 ++++++++++++ src/basics/Mapping.sol | 80 +++++++++ src/basics/Payable.sol | 119 ++++++++++++++ src/basics/Primitives.sol | 61 +++++++ src/basics/SendEther.sol | 106 ++++++++++++ src/basics/SimpleStorage.sol | 43 +++++ src/basics/StructTodos.sol | 42 +++++ src/basics/TransientStorage.sol | 99 +++++++++++ src/basics/Variables.sol | 52 ++++++ src/basics/ViewAndPure.sol | 44 +++++ src/basics/Visibility.sol | 119 ++++++++++++++ src/basics/getAjob.txt | 219 +++++++++++++++++++++++++ src/rounding-error/StakingRewards.sol | 40 +++++ src/rounding-error/VulnerableVault.sol | 45 +++++ test/RoundingErrorTest.t.sol | 74 +++++++++ 36 files changed, 2879 insertions(+) create mode 100644 src/basics/AbiDecode.sol create mode 100644 src/basics/AbiEncode.sol create mode 100644 src/basics/Array.sol create mode 100644 src/basics/CallExample.sol create mode 100644 src/basics/Constants.sol create mode 100644 src/basics/ConstructorExample.sol create mode 100644 src/basics/Counter.sol create mode 100644 src/basics/DataLocations.sol create mode 100644 src/basics/DelegateCall.sol create mode 100644 src/basics/Enum.sol create mode 100644 src/basics/Error.sol create mode 100644 src/basics/EtherUnits.sol create mode 100644 src/basics/EventExample.sol create mode 100644 src/basics/Fallback.sol create mode 100644 src/basics/ForWhile.sol create mode 100644 src/basics/FunctionModifier.sol create mode 100644 src/basics/Gas.sol create mode 100644 src/basics/HashFunction.sol create mode 100644 src/basics/HelloWorld.sol create mode 100644 src/basics/IfElse.sol create mode 100644 src/basics/Immutable.sol create mode 100644 src/basics/Interface.sol create mode 100644 src/basics/Mapping.sol create mode 100644 src/basics/Payable.sol create mode 100644 src/basics/Primitives.sol create mode 100644 src/basics/SendEther.sol create mode 100644 src/basics/SimpleStorage.sol create mode 100644 src/basics/StructTodos.sol create mode 100644 src/basics/TransientStorage.sol create mode 100644 src/basics/Variables.sol create mode 100644 src/basics/ViewAndPure.sol create mode 100644 src/basics/Visibility.sol create mode 100644 src/basics/getAjob.txt create mode 100644 src/rounding-error/StakingRewards.sol create mode 100644 src/rounding-error/VulnerableVault.sol create mode 100644 test/RoundingErrorTest.t.sol diff --git a/src/basics/AbiDecode.sol b/src/basics/AbiDecode.sol new file mode 100644 index 0000000..a8469fe --- /dev/null +++ b/src/basics/AbiDecode.sol @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +/// @title ABI Decode Examples +/// @author Ishan Lakhwani +/// @notice Shows how to encode and decode complex data structures + +/* + * UNDERSTANDING ABI DECODE ๐Ÿ“ฆ + * + * ABI decoding converts encoded bytes back into Solidity types: + * - Reverses the encoding process + * - Must know the exact types to decode + * - Works with complex data structures + * + * Visual Process: + * + * Encode: Data Types โ†’ [๐Ÿ“ฆ Encoded Bytes] + * Decode: [๐Ÿ“ฆ Encoded Bytes] โ†’ Original Data + * + * Example Flow: + * uint(123), address(0x...) โ†’ 0x000...7b... โ†’ uint(123), address(0x...) + */ +contract AbiDecode { + /* + * Complex data structure example + * Shows nested data that needs encoding/decoding + * + * Layout: + * MyStruct { + * string name; // Dynamic type + * uint256[2] nums; // Fixed array + * } + */ + struct MyStruct { + string name; + uint256[2] nums; + } + + /* + * Encode multiple parameters including complex types + * + * Parameters: + * - x: Simple uint256 + * - addr: Ethereum address + * - arr: Dynamic uint256 array + * - myStruct: Custom struct + * + * Visual Encoding: + * [x][addr][arr_length][arr_data][struct_data] โ†’ bytes + */ + function encode(uint256 x, address addr, uint256[] calldata arr, MyStruct calldata myStruct) + external + pure + returns (bytes memory) + { + // Pack all parameters into a single bytes array + return abi.encode(x, addr, arr, myStruct); + } + + /* + * Decode bytes back into original types + * + * Flow: + * 1. Take encoded bytes + * 2. Specify expected types + * 3. Get original values + * + * Visual Decoding: + * bytes โ†’ [Decoder] โ†’ Original Types + * Must match โ†‘ encoding order + */ + function decode(bytes calldata data) + external + pure + returns (uint256 x, address addr, uint256[] memory arr, MyStruct memory myStruct) + { + // Decode bytes into original types + // Order and types must match encoding exactly + (x, addr, arr, myStruct) = abi.decode(data, (uint256, address, uint256[], MyStruct)); + } +} + +/* + * ABI DECODE BEST PRACTICES: + * + * 1. Type Safety: + * - Match types exactly + * - Order matters + * - Handle dynamic types carefully + * + * 2. Common Use Cases: + * - Cross-contract communication + * - Batch processing + * - Data verification + * + * 3. Error Handling: + * - Validate decoded data + * - Handle malformed input + * - Check array lengths + * + * Visual Type Matching: + * + * Encode: (uint256, address, uint[]) โ†’ bytes + * Decode: bytes โ†’ (uint256, address, uint[]) โœ… + * Wrong: bytes โ†’ (address, uint256, uint[]) โŒ + */ diff --git a/src/basics/AbiEncode.sol b/src/basics/AbiEncode.sol new file mode 100644 index 0000000..31374ab --- /dev/null +++ b/src/basics/AbiEncode.sol @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +/// @title ABI Encoding Examples +/// @author Ishan Lakhwani +/// @notice Shows different ways to encode function calls + +/* + * UNDERSTANDING ABI ENCODING ๐Ÿ“ + * + * ABI (Application Binary Interface) encoding converts function calls and parameters + * into bytes that can be sent to contracts. + * + * Three main encoding methods: + * 1. encodeWithSignature: Uses string signature + * 2. encodeWithSelector: Uses function selector + * 3. encodeCall: Type-safe encoding (recommended) + * + * Visual Example: + * + * Function Call โ†’ Encoded Bytes + * transfer(0x123.., 100) โ†’ 0xa9059cbb... + * [selector][params] + */ + +// Interface defines the function we want to call +interface IERC20 { + function transfer(address, uint256) external; +} + +// Mock token contract for example +contract Token { + function transfer(address, uint256) external {} +} + +contract AbiEncode { + /* + * Generic function to execute encoded calls + * Like a universal remote that can send any command + * + * Flow: + * 1. Receive encoded data + * 2. Send to target contract + * 3. Check if call succeeded + */ + function test(address _contract, bytes calldata data) external { + (bool ok,) = _contract.call(data); + require(ok, "call failed"); + } + + /* + * Method 1: encodeWithSignature + * Uses string function signature + * + * Risks: + * - Typos won't be caught at compile time + * - "transfer(address,uint256)" must be exact + * + * Visual: + * "transfer(address,uint256)" โ†’ 0xa9059cbb (selector) + */ + function encodeWithSignature(address to, uint256 amount) external pure returns (bytes memory) { + // Manually specify function signature + return abi.encodeWithSignature("transfer(address,uint256)", to, amount); + } + + /* + * Method 2: encodeWithSelector + * Uses function selector directly + * + * Better than signature because: + * - No string manipulation + * - Selector is computed at compile time + * + * Visual: + * IERC20.transfer.selector โ†’ 0xa9059cbb + */ + function encodeWithSelector(address to, uint256 amount) external pure returns (bytes memory) { + // Use interface to get selector + return abi.encodeWithSelector(IERC20.transfer.selector, to, amount); + } + + /* + * Method 3: encodeCall (Recommended) + * Type-safe encoding using interface + * + * Advantages: + * - Compile-time type checking + * - No manual signature/selector needed + * - Safest option + * + * Visual: + * IERC20.transfer โ†’ Type-checked โ†’ Encoded bytes + */ + function encodeCall(address to, uint256 amount) external pure returns (bytes memory) { + // Most safe: compiler checks types + return abi.encodeCall(IERC20.transfer, (to, amount)); + } +} + +/* + * ABI ENCODING BEST PRACTICES: + * + * 1. Safety: + * - Use encodeCall when possible (type-safe) + * - Verify encoded data carefully + * - Test with actual contracts + * + * 2. Common Use Cases: + * - Contract interaction + * - Proxy contracts + * - Batch transactions + * + * 3. Method Selection: + * encodeCall โ†’ Known interface, type safety needed + * encodeWithSelector โ†’ Known selector, no interface + * encodeWithSignature โ†’ Dynamic/unknown functions + * + * Visual Encoding Process: + * + * Function + Args โ†’ ABI Encode โ†’ Selector + Params โ†’ Contract + * transfer(addr, 100) โ†’ 0xa9059cbb + params โ†’ Target + */ diff --git a/src/basics/Array.sol b/src/basics/Array.sol new file mode 100644 index 0000000..6af2c54 --- /dev/null +++ b/src/basics/Array.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +/// @title Array Examples +/// @author Ishan Lakhwani +/// @notice Shows different types of arrays and how to use them + +/* + * ARRAYS IN SOLIDITY + * + * Think of arrays like a list of items: + * + * Dynamic Array (can grow/shrink): + * [1, 2, 3, 4, 5, ...] + * + * Fixed Array (size can't change): + * [_, _, _, _] (4 slots, fixed) + * + * Visual Example: + * +---+---+---+---+---+ + * | 1 | 2 | 3 | 4 | 5 | Dynamic: can add more โ†’ + * +---+---+---+---+---+ + * + * +---+---+---+---+ + * | 1 | 2 | 3 | 4 | Fixed: size stays at 4 + * +---+---+---+---+ + */ +contract Array { + // Can grow or shrink + uint256[] public arr; + + // Starts with values + uint256[] public arr2 = [1, 2, 3]; + + // Fixed size of 10 + uint256[10] public myFixedSizeArr; + + // Get one item + function get(uint256 i) public view returns (uint256) { + return arr[i]; + } + + // Get whole array + function getArr() public view returns (uint256[] memory) { + return arr; // Warning: expensive for big arrays! + } + + // Add item to end + function push(uint256 i) public { + arr.push(i); + } + + // Remove last item + function pop() public { + arr.pop(); + } + + // Get array size + function getLength() public view returns (uint256) { + return arr.length; + } + + // Clear item at position + function remove(uint256 index) public { + // Just sets to 0, doesn't shrink array + delete arr[index]; + } + + // Create array in memory + function examples() external pure { + // Fixed size of 5 in memory + uint256[] memory a = new uint256[](5); + } +} diff --git a/src/basics/CallExample.sol b/src/basics/CallExample.sol new file mode 100644 index 0000000..da2de74 --- /dev/null +++ b/src/basics/CallExample.sol @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +/// @title Low-Level Call Examples +/// @author Ishan Lakhwani +/// @notice Shows how to make low-level calls between contracts + +/* + * UNDERSTANDING LOW-LEVEL CALLS ๐Ÿ“ž + * + * Low-level calls allow contracts to interact dynamically: + * - Can call functions that don't exist + * - Can send ETH with the call + * - More flexible but more dangerous + * + * Visual Call Flow: + * encode call decode + * Caller โ†’ [function signature + data] โ†’ Receiver โ†’ [result] + * "foo(string,uint256)" + */ + +/* + * Receiver Contract + * Like a phone that can receive calls + */ +contract Receiver { + // Event to log incoming calls + event Received( // Who called + // How much ETH sent + // What message + address caller, uint256 amount, string message); + + /* + * Fallback Function + * Called when: + * - Function doesn't exist + * - Raw call data sent + * + * Like an answering machine + */ + fallback() external payable { + emit Received(msg.sender, msg.value, "Fallback was called"); + } + + /* + * Regular Function + * Can be called normally or via low-level call + * + * Like a specific phone extension + */ + function foo(string memory _message, uint256 _x) public payable returns (uint256) { + // Log the call details + emit Received(msg.sender, msg.value, _message); + // Return modified value + return _x + 1; + } +} + +/* + * Caller Contract + * Like a phone making calls + */ +contract Caller { + // Event to log call results + event Response(bool success, bytes data); + + /* + * Test calling existing function + * + * Flow: + * 1. Encode function signature and parameters + * 2. Make the call with ETH and gas + * 3. Get success/failure and return data + * + * Visual: + * Caller โ†’ encode("foo(string,uint256)") โ†’ Receiver + * โ† success/data โ† + */ + function testCallFoo(address payable _addr) public payable { + // Encode the function call + // "foo(string,uint256)" + parameters + (bool success, bytes memory data) = _addr.call{ + value: msg.value, // Send ETH + gas: 5000 // Limit gas + }( + abi.encodeWithSignature( + "foo(string,uint256)", // Function signature + "call foo", // First parameter + 123 // Second parameter + ) + ); + + // Log the result + emit Response(success, data); + } + + /* + * Test calling non-existent function + * Will trigger the fallback function + * + * Visual: + * Caller โ†’ "doesNotExist()" โ†’ Receiver's fallback + */ + function testCallDoesNotExist(address payable _addr) public payable { + // Try to call non-existent function + (bool success, bytes memory data) = _addr.call{value: msg.value}(abi.encodeWithSignature("doesNotExist()")); + + // Log the result + emit Response(success, data); + } +} + +/* + * LOW-LEVEL CALL BEST PRACTICES: + * + * 1. Security: + * - Always check return values + * - Be careful with external calls + * - Consider reentrancy risks + * + * 2. Gas: + * - Set gas limits when needed + * - Handle out-of-gas scenarios + * + * 3. Error Handling: + * - Check success boolean + * - Handle return data carefully + * + * 4. Documentation: + * - Document expected function signatures + * - Log important call data + * + * Visual Call Process: + * + * 1. Normal Call: + * Caller โ†’ foo("hello", 123) โ†’ Receiver โ†’ Return Value + * + * 2. Failed Call: + * Caller โ†’ unknownFunction() โ†’ Fallback โ†’ Log Event + */ diff --git a/src/basics/Constants.sol b/src/basics/Constants.sol new file mode 100644 index 0000000..9d88fa7 --- /dev/null +++ b/src/basics/Constants.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +contract Constants { + // coding convention to uppercase constant variables + address public constant MY_ADDRESS = 0x777788889999AaAAbBbbCcccddDdeeeEfFFfCcCc; + uint256 public constant MY_UINT = 123; +} diff --git a/src/basics/ConstructorExample.sol b/src/basics/ConstructorExample.sol new file mode 100644 index 0000000..f581642 --- /dev/null +++ b/src/basics/ConstructorExample.sol @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +/// @title Constructor and Inheritance Examples +/// @author Ishan Lakhwani +/// @notice Shows different ways to use constructors with inheritance + +/* + * UNDERSTANDING CONSTRUCTORS AND INHERITANCE ๐Ÿ—๏ธ + * + * Think of it like building a house: + * - Constructor is like the initial setup/building process + * - Inheritance is like building on top of existing foundations + * + * Visual Example of Inheritance: + * + * Base Contracts + * +--------+ +--------+ + * | X | | Y | <- Parent contracts + * +--------+ +--------+ + * โ†“ โ†“ + * +----------+ + * | B | <- Child contract + * +----------+ + */ + +// Base Contract X (Parent) +contract X { + string public name; + + // Constructor for X + // Like setting up the foundation + constructor(string memory _name) { + name = _name; + } +} + +// Base Contract Y (Parent) +contract Y { + string public text; + + // Constructor for Y + // Like setting up another foundation piece + constructor(string memory _text) { + text = _text; + } +} + +/* + * INHERITANCE METHOD 1: Fixed Values ๐ŸŽฏ + * + * Like building a house with pre-selected materials + * Values are hardcoded when deploying + */ +contract B is X("Input to X"), Y("Input to Y") { +// B's constructor is empty +// Parent constructors are called with fixed values +// Like saying "always use brick for X and wood for Y" +} + +/* + * INHERITANCE METHOD 2: Flexible Values ๐Ÿ”„ + * + * Like building a house where you choose materials later + * Values are passed when deploying + */ +contract C is X, Y { + // Pass values to parent constructors + constructor( + string memory _name, // Value for X + string memory _text // Value for Y + ) X(_name) Y(_text) {} +} + +/* + * INHERITANCE METHOD 3: Fixed Order โฌ‡๏ธ + * + * Order of constructor calls follows inheritance list + * X is called first, then Y + */ +contract D is X, Y { + constructor() X("X was called") Y("Y was called") {} + // Execution order: X โ†’ Y +} + +/* + * IMPORTANT NOTE ON ORDER: + * Even though Y is listed first in constructor, + * X will still be called first because of inheritance order (is X, Y) + */ +contract E is X, Y { + constructor() Y("Y was called") X("X was called") {} + // Still executes: X โ†’ Y (order in constructor doesn't matter) +} + +/* + * CONSTRUCTOR BEST PRACTICES: + * + * 1. Keep initialization logic simple + * 2. Be clear about inheritance order + * 3. Document parameter requirements + * 4. Consider using flexible values when needed + * + * Think of it like building instructions: + * - Be clear about the order + * - Document what materials (parameters) are needed + * - Make it flexible when needed + */ diff --git a/src/basics/Counter.sol b/src/basics/Counter.sol new file mode 100644 index 0000000..f9b3ab2 --- /dev/null +++ b/src/basics/Counter.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +contract Counter { + uint256 public count; + + // Function to get the current count + function get() public view returns (uint256) { + return count; + } + + // Function to increment count by 1 + function inc() public { + count += 1; + } + + // Function to decrement count by 1 + function dec() public { + // This function will fail if count = 0 + count -= 1; + } +} diff --git a/src/basics/DataLocations.sol b/src/basics/DataLocations.sol new file mode 100644 index 0000000..2f1345b --- /dev/null +++ b/src/basics/DataLocations.sol @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +/// @title Data Locations in Solidity +/// @author Ishan Lakhwani +/// @notice Shows different ways data can be stored + +/* + * DATA LOCATIONS: Where variables live + * + * 1. Storage (๐Ÿ’พ Permanent) + * - Like writing to a hard drive + * - Persists between function calls + * - Expensive (costs more gas) + * - Example: Your bank account balance + * + * 2. Memory (๐ŸŽฎ Temporary) + * - Like computer RAM + * - Only exists during function call + * - Cheaper than storage + * - Example: Calculator's temporary display + * + * 3. Calldata (๐Ÿ“จ Read-only) + * - Like a sealed envelope + * - Can't be modified + * - Cheapest option + * - Example: Email that you can read but can't edit + * + * Visual Example: + * +------------+-------------+------------+-------------+----------------+ + * | | Persistent? | Modifiable | Gas Cost | Real Example | + * +------------+-------------+------------+-------------+----------------+ + * | Storage ๐Ÿ’พ | โœ… | โœ… | Expensive | Hard Drive | + * | Memory ๐ŸŽฎ | โŒ | โœ… | Medium | RAM | + * | Calldata๐Ÿ“จ | โŒ | โŒ | Cheap | CD-ROM | + * +------------+-------------+------------+-------------+----------------+ + * + * Think of it like a shop: + * - Storage: Items in the warehouse (permanent) + * - Memory: Shopping cart (temporary, can modify) + * - Calldata: Shopping list (read-only) + */ +contract DataLocations { + // These variables are always in storage + uint256[] public arr; // Like items in warehouse + mapping(uint256 => address) map; // Like a customer database + + // Custom data structure + struct MyStruct { + uint256 foo; // Like a product details form + } + + // Mapping of structs in storage + mapping(uint256 => MyStruct) myStructs; // Like a filing cabinet + + function f() public { + // Example 1: Storage References + // Like getting items from different warehouse sections + _f(arr, map, myStructs[1]); + + // Example 2: Direct Storage Access + // Like working directly with warehouse inventory + MyStruct storage myStruct = myStructs[1]; + // Changes to myStruct will be permanent + + // Example 3: Memory Copy + // Like making a temporary copy of product details + MyStruct memory myMemStruct = MyStruct(0); + // Changes to myMemStruct will be lost after function ends + } + + // Storage References Example + // Like giving someone access to specific warehouse sections + function _f( + uint256[] storage _arr, // Access to array shelf + mapping(uint256 => address) storage _map, // Access to customer records + MyStruct storage _myStruct // Access to one product file + ) internal { + // Any changes here affect the real storage data + // Like making changes to actual warehouse inventory + } + + // Memory Example + // Like working with a temporary copy + function g(uint256[] memory _arr) public pure returns (uint256[] memory) { + // _arr is a temporary copy we can modify + // Like working with a draft document + return _arr; + } + + // Calldata Example + // Like receiving a sealed package + function h(uint256[] calldata _arr) external { + // Can read _arr but can't modify it + // Like reading instructions on a sealed envelope + } +} diff --git a/src/basics/DelegateCall.sol b/src/basics/DelegateCall.sol new file mode 100644 index 0000000..2c81e67 --- /dev/null +++ b/src/basics/DelegateCall.sol @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +/// @title DelegateCall Example +/// @author Ishan Lakhwani +/// @notice Shows how delegatecall works and its differences from regular calls + +/* + * UNDERSTANDING DELEGATECALL ๐Ÿ“š + * + * delegatecall is like borrowing code but using your own storage: + * - Uses logic from target contract + * - But executes in caller's context + * - Storage layout must match! + * + * Visual Storage Comparison: + * + * Regular call: + * Contract A Contract B + * +--------+ +--------+ + * |num: 0 | call |num: 5 | // Each contract uses its own storage + * |sender:A| โ†’ |sender:A| + * +--------+ +--------+ + * + * delegatecall: + * Contract A Contract B + * +--------+ +--------+ + * |num: 5 | delegate|num: 0 | // A's storage is modified using B's code + * |sender:A| โ†’ |sender:B| + * +--------+ +--------+ + */ + +/* + * Target Contract (Logic Contract) + * Like a library of functions that others can borrow + */ +contract B { + // Storage layout MUST match Contract A + uint256 public num; // slot 0 + address public sender; // slot 1 + uint256 public value; // slot 2 + + /* + * Function that modifies state + * Will modify caller's state when used with delegatecall + */ + function setVars(uint256 _num) public payable { + num = _num; // Changes storage slot 0 + sender = msg.sender; // Changes storage slot 1 + value = msg.value; // Changes storage slot 2 + } +} + +/* + * Caller Contract (Storage Contract) + * Uses delegatecall to borrow logic from Contract B + */ +contract A { + // Storage layout MUST match Contract B + uint256 public num; // slot 0 + address public sender; // slot 1 + uint256 public value; // slot 2 + + /* + * Using delegatecall + * Borrows logic but uses our storage + * + * Flow: + * 1. A's storage is used + * 2. B's code is executed + * 3. msg.sender remains original caller + */ + function setVarsDelegateCall(address _contract, uint256 _num) public payable { + // Make delegatecall to B's setVars function + (bool success, bytes memory data) = _contract.delegatecall(abi.encodeWithSignature("setVars(uint256)", _num)); + } + + /* + * Using regular call (for comparison) + * Executes in target contract's context + * + * Flow: + * 1. B's storage is used + * 2. B's code is executed + * 3. msg.sender becomes contract A + */ + function setVarsCall(address _contract, uint256 _num) public payable { + (bool success, bytes memory data) = + _contract.call{value: msg.value}(abi.encodeWithSignature("setVars(uint256)", _num)); + } +} + +/* + * DELEGATECALL BEST PRACTICES: + * + * 1. Storage Layout: + * - Must match exactly between contracts + * - Include all variables in same order + * - Consider using proxies pattern + * + * 2. Security: + * - Be careful with trusted contracts + * - Validate target contract + * - Check return values + * + * 3. Common Use Cases: + * - Proxy patterns + * - Upgradeable contracts + * - Logic libraries + * + * Visual Context Comparison: + * + * msg.sender in regular call: + * User โ†’ Contract A โ†’ Contract B + * msg.sender msg.sender + * + * msg.sender in delegatecall: + * User โ†’ Contract A โŸฒ Contract B + * msg.sender (same msg.sender) + */ diff --git a/src/basics/Enum.sol b/src/basics/Enum.sol new file mode 100644 index 0000000..015bb1b --- /dev/null +++ b/src/basics/Enum.sol @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +/// @title Enum Example with Order Status +/// @author Ishan Lakhwani +/// @notice Shows how to use enums for status tracking + +/* + * ENUMS: Like a fixed set of options + * + * Think of it like a shipping status: + * + * Visual Example: + * +------------+ + * | ๐Ÿ“ฆ Order | + * +------------+ + * โฌ‡๏ธ + * [0] Pending (Default) + * โฌ‡๏ธ + * [1] Shipped + * โฌ‡๏ธ + * [2] Accepted + * โฌ‡๏ธ + * [3] Rejected + * โฌ‡๏ธ + * [4] Canceled + * + * Each status has a number behind the scenes: + * Pending = 0 (default) + * Shipped = 1 + * Accepted = 2 + * Rejected = 3 + * Canceled = 4 + */ +contract Enum { + // Define all possible status values + enum Status { + Pending, // 0 + Shipped, // 1 + Accepted, // 2 + Rejected, // 3 + Canceled // 4 + + } + + // Current status (starts at Pending) + Status public status; + + // Get current status + function get() public view returns (Status) { + return status; + } + + // Update to a new status + function set(Status _status) public { + status = _status; + } + + // Special function to mark as canceled + function cancel() public { + status = Status.Canceled; + } + + // Reset back to Pending + function reset() public { + delete status; // same as status = Status.Pending + } +} diff --git a/src/basics/Error.sol b/src/basics/Error.sol new file mode 100644 index 0000000..979b7e0 --- /dev/null +++ b/src/basics/Error.sol @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +/// @title Error Handling in Solidity +/// @author Ishan Lakhwani +/// @notice Shows different ways to handle errors and validate conditions + +/* + * UNDERSTANDING ERROR HANDLING ๐Ÿšซ + * + * Three main ways to handle errors in Solidity: + * + * 1. require(): Like a bouncer at a club + * - Checks condition at entrance + * - Returns gas if condition fails + * - Good for input validation + * + * 2. revert(): Like an emergency stop button + * - Stops execution and returns gas + * - Can be used anywhere + * - Good for complex conditions + * + * 3. assert(): Like a safety check + * - Checks for conditions that should NEVER be false + * - Uses all gas if fails + * - Good for invariants + * + * Visual Comparison: + * +------------+------------------+----------------+------------------+ + * | | Gas Refund | Use Case | Error Message | + * +------------+------------------+----------------+------------------+ + * | require() | โœ… Returns gas | Input checking | Custom message | + * | revert() | โœ… Returns gas | Complex logic | Custom message | + * | assert() | โŒ Uses all gas | Invariants | Default message | + * +------------+------------------+----------------+------------------+ + */ +contract Error { + /* + * Example 1: require() + * Like checking ID at a club entrance + */ + function testRequire(uint256 _i) public pure { + // Check if input meets our condition + require(_i > 10, "Input must be greater than 10"); + // some code + // If we get past require, _i is definitely > 10 + } + + /* + * Example 2: revert() + * Like hitting the emergency stop + */ + function testRevert(uint256 _i) public pure { + // More complex condition checking + if (_i <= 10) { + revert("Input must be greater than 10"); + } + // If we get here, _i is definitely > 10 + } + + // State variable for assert example + uint256 public num; + + /* + * Example 3: assert() + * Like a safety check that should never fail + */ + function testAssert() public view { + // This should ALWAYS be true + // If it's false, we have a serious bug + assert(num == 0); + } + + /* + * Example 4: Custom Errors + * Like having specific error codes + * More gas efficient than string messages + */ + // Define custom error with parameters + error InsufficientBalance(uint256 balance, uint256 withdrawAmount); + + function testCustomError(uint256 _withdrawAmount) public view { + uint256 bal = address(this).balance; + + // If withdrawal amount is too high + if (bal < _withdrawAmount) { + // Revert with custom error and data + revert InsufficientBalance({balance: bal, withdrawAmount: _withdrawAmount}); + } + // If we get here, withdrawal amount is valid + } +} + +/* + * ERROR HANDLING BEST PRACTICES: + * + * 1. Use require() for: + * - Input validation + * - Condition checking at start of function + * - Access control + * + * 2. Use revert() for: + * - Complex conditions + * - Multiple conditions + * - Custom errors + * + * 3. Use assert() for: + * - Invariant checking + * - Conditions that should never be false + * - Internal errors + * + * 4. Custom Errors: + * - More gas efficient than strings + * - Can include error data + * - Better for debugging + */ diff --git a/src/basics/EtherUnits.sol b/src/basics/EtherUnits.sol new file mode 100644 index 0000000..d267954 --- /dev/null +++ b/src/basics/EtherUnits.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +/// @title Ether Units Demonstration +/// @author Ishan Lakhwani +/// @notice Shows different denominations of Ether +/// @dev Demonstrates wei, gwei, and ether conversions + +/* + * UNDERSTANDING ETHER UNITS + * + * Ether (ETH) can be expressed in different denominations: + * - wei: smallest unit of ether + * - gwei: giga-wei, commonly used for gas prices + * - ether: main unit used for trading + * + * Conversion Chart: + * 1 ether = 1,000,000,000 gwei (10^9) + * 1 ether = 1,000,000,000,000,000,000 wei (10^18) + * 1 gwei = 1,000,000,000 wei (10^9) + */ +contract EtherUnits { + // Wei is the base unit + uint256 public oneWei = 1 wei; + // Verify that 1 wei is indeed 1 + bool public isOneWei = (oneWei == 1); + + // Gwei (giga-wei) is commonly used for gas prices + uint256 public oneGwei = 1 gwei; // 1,000,000,000 wei + // Verify that 1 gwei equals 10^9 wei + bool public isOneGwei = (oneGwei == 1e9); + + // Ether is the main unit we typically work with + uint256 public oneEther = 1 ether; // 1,000,000,000,000,000,000 wei + // Verify that 1 ether equals 10^18 wei + bool public isOneEther = (oneEther == 1e18); +} diff --git a/src/basics/EventExample.sol b/src/basics/EventExample.sol new file mode 100644 index 0000000..f8326cc --- /dev/null +++ b/src/basics/EventExample.sol @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +/// @title Event Examples in Solidity +/// @author Ishan Lakhwani +/// @notice Shows how to use events for logging and tracking + +/* + * UNDERSTANDING EVENTS ๐Ÿ“ข + * + * Events are like announcements or notifications in blockchain: + * - They're like a newspaper's announcements section + * - Data is stored in transaction logs + * - Cannot be read by smart contracts + * - Useful for front-end applications + * + * Visual Example of Event Flow: + * + * Contract Transaction Log Frontend/Dapp + * ๐Ÿ“ โ†’ ๐Ÿ“œ Event Data โ†’ ๐Ÿ–ฅ๏ธ UI + * (emits) (stores logs) (displays info) + * + * Indexed vs Non-Indexed Parameters: + * +----------------+-------------------------+------------------------+ + * | | Indexed (max 3) | Non-Indexed | + * +----------------+-------------------------+------------------------+ + * | Search | โœ… Can search for | โŒ Cannot search | + * | Gas Cost | ๐Ÿ’ฐ More expensive | ๐Ÿ’ฐ Cheaper | + * | Use Case | ๐Ÿ” Filtering events | ๐Ÿ“ Storing data | + * +----------------+-------------------------+------------------------+ + */ +contract Event { + /* + * Event Declaration: + * - 'sender' is indexed (searchable) + * - 'message' is not indexed (just stored) + * + * Like a newspaper with: + * - Indexed: Section number (easy to find) + * - Non-indexed: Article content (just for reading) + */ + event Log( // Like the author's signature (searchable) + // Like the message content + address indexed sender, string message); + + // Simple event with no parameters + event AnotherLog(); + + /* + * Function that emits events + * Like publishing multiple announcements + */ + function test() public { + // First announcement + emit Log(msg.sender, "Hello World!"); + + // Second announcement + emit Log(msg.sender, "Hello EVM!"); + + // Simple notification + emit AnotherLog(); + } +} + +/* + * EVENT BEST PRACTICES: + * + * 1. Use Events for: + * - Important state changes + * - Frontend notifications + * - Contract activity monitoring + * + * 2. Indexing: + * - Index parameters you want to search for + * - Maximum 3 indexed parameters + * - Index addresses and IDs, not strings + * + * 3. Naming: + * - Past tense (Transfer, Approval, Deposit) + * - Clear and descriptive + * + * Example Use Cases: + * - Token transfers: Transfer(from, to, amount) + * - State changes: StatusChanged(oldStatus, newStatus) + * - Access events: AdminAdded(newAdmin) + */ diff --git a/src/basics/Fallback.sol b/src/basics/Fallback.sol new file mode 100644 index 0000000..d6b8744 --- /dev/null +++ b/src/basics/Fallback.sol @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +/// @title Fallback and Receive Functions +/// @author Ishan Lakhwani +/// @notice Shows how fallback and receive functions handle incoming ETH + +/* + * UNDERSTANDING FALLBACK & RECEIVE ๐Ÿ“จ + * + * How Contract Receives ETH: + * + * Incoming Transaction + * โฌ‡๏ธ + * Has ETH been sent? + * / \ + * Yes No + * / \ + * Is msg.data empty? fallback() + * / \ + * Yes No + * / \ + * receive() fallback() + * + * Visual Function Selection: + * +------------------+------------------+------------------+ + * | Condition | msg.data empty? | Function Called | + * +------------------+------------------+------------------+ + * | Sending ETH | โœ… | receive() | + * | Sending ETH | โŒ | fallback() | + * | No ETH | Either | fallback() | + * +------------------+------------------+------------------+ + */ +contract Fallback { + // Event to track which function was called and remaining gas + event Log(string func, uint256 gas); + + /* + * RECEIVE FUNCTION + * Called when: + * 1. ETH is sent to contract + * 2. msg.data is empty + * + * Example: Simple ETH transfer + * User โ†’ Contract + * 1 ETH (no data) + */ + receive() external payable { + // Log function name and remaining gas + emit Log("receive", gasleft()); + } + + /* + * FALLBACK FUNCTION + * Called when: + * 1. No other function matches call + * 2. ETH sent with data + * 3. No receive() exists + * + * Example: ETH transfer with data + * User โ†’ Contract + * 1 ETH (with data) + */ + fallback() external payable { + // Log function name and remaining gas + emit Log("fallback", gasleft()); + } + + /* + * View contract's ETH balance + * Useful for checking if ETH was received + */ + function getBalance() public view returns (uint256) { + return address(this).balance; + } +} + +/* + * COMMON USE CASES: + * + * receive(): + * - Accept ETH payments + * - Simple ETH transfers + * - No additional logic needed + * + * fallback(): + * - Handle unknown function calls + * - Accept ETH with data + * - Custom function forwarding + * + * Visual Example of Calls: + * + * 1. Regular ETH Transfer: + * send 1 ETH โ†’ receive() + * + * 2. ETH + Data Transfer: + * send 1 ETH + data โ†’ fallback() + * + * 3. Unknown Function: + * call unknownFunc() โ†’ fallback() + * + * BEST PRACTICES: + * + * 1. Keep these functions simple + * 2. Use events for monitoring + * 3. Consider gas limitations + * 4. Handle errors appropriately + * 5. Document expected behavior + */ diff --git a/src/basics/ForWhile.sol b/src/basics/ForWhile.sol new file mode 100644 index 0000000..603b98f --- /dev/null +++ b/src/basics/ForWhile.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +/// @title Loop Control Structures in Solidity +/// @author Ishan Lakhwani +/// @notice Demonstrates different types of loops and control statements +/// @dev Shows for loops, while loops, continue, and break statements + +contract Loop { + /* + * @notice Demonstrates different types of loops and control flow + * @dev This is a pure function (doesn't modify or read state) + * + * Gas Consideration: + * - Loops can be expensive in terms of gas + * - Always ensure loops have a finite bound + * - Be careful with large loops as they might hit gas limits + */ + function loop() public pure { + /* + * For Loop Structure: + * - Initialization: uint256 i = 0 + * - Condition: i < 10 + * - Iteration: i++ + */ + for (uint256 i = 0; i < 10; i++) { + if (i == 3) { + // continue skips the rest of this iteration + // and moves to the next one + continue; + } + if (i == 5) { + // break exits the loop completely + break; + } + } + + /* + * While Loop: + * - Simpler structure but needs manual counter + * - Runs as long as condition is true + * - Must ensure the condition eventually becomes false + */ + uint256 j; + while (j < 10) { + j++; + } + } +} diff --git a/src/basics/FunctionModifier.sol b/src/basics/FunctionModifier.sol new file mode 100644 index 0000000..caee74d --- /dev/null +++ b/src/basics/FunctionModifier.sol @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +/// @title Function Modifier Examples +/// @author Ishan Lakhwani +/// @notice Shows how to use modifiers to control function access and behavior + +/* + * UNDERSTANDING FUNCTION MODIFIERS ๐Ÿ›ก๏ธ + * + * Think of modifiers like security checks before entering a room: + * + * Visual Example: + * + * Function Call + * โฌ‡๏ธ + * +----------------+ + * | Modifier | <- Security Check + * | Check | + * +----------------+ + * โฌ‡๏ธ + * +----------------+ + * | Function | <- Only if check passes + * | Code | + * +----------------+ + * + * Real World Examples: + * - onlyOwner: Like an ID check at a VIP room + * - validAddress: Like verifying a real address exists + * - noReentrancy: Like a "one person at a time" rule + */ +contract FunctionModifier { + // Contract owner's address + address public owner; + // Example state variable + uint256 public x = 10; + // Lock flag for reentrancy guard + bool public locked; + + /* + * Constructor: Sets up the contract + * Like setting up a new store and making yourself the owner + */ + constructor() { + // The person deploying the contract becomes the owner + owner = msg.sender; + } + + /* + * MODIFIER 1: onlyOwner ๐Ÿ‘‘ + * Checks if caller is the owner + * Like a VIP check at an exclusive club + */ + modifier onlyOwner() { + require(msg.sender == owner, "Not owner"); + // The underscore is where the function code gets inserted + _; + } + + /* + * MODIFIER 2: validAddress โœ… + * Checks if an address is valid + * Like verifying a delivery address isn't empty + */ + modifier validAddress(address _addr) { + require(_addr != address(0), "Not valid address"); + _; + } + + /* + * Function that uses multiple modifiers + * Will check both owner status AND address validity + */ + function changeOwner(address _newOwner) + public + onlyOwner // First checks if current owner + validAddress(_newOwner) // Then validates new address + { + owner = _newOwner; + } + + /* + * MODIFIER 3: noReentrancy ๐Ÿ”’ + * Prevents function from being called again while running + * Like a "Do Not Disturb" sign while cleaning a room + */ + modifier noReentrancy() { + // Check if already locked + require(!locked, "No reentrancy"); + // Lock the door + locked = true; + // Run the function + _; + // Unlock after function completes + locked = false; + } + + /* + * Example of reentrancy protection + * + * Without protection, this could be dangerous: + * decrement(3) would call: + * โ†’ decrement(2) + * โ†’ โ†’ decrement(1) + * + * With protection: Only one call at a time allowed + */ + function decrement(uint256 i) public noReentrancy { + x -= i; + if (i > 1) { + // Try to call itself again + decrement(i - 1); + } + } +} + +/* + * MODIFIER BEST PRACTICES: + * + * 1. Keep modifiers simple + * 2. Use them for common checks + * 3. Don't modify state in modifiers (except reentrancy locks) + * 4. Document what each modifier does + * + * Common Use Cases: + * - Access control (onlyOwner, onlyAdmin) + * - Input validation (validAddress, positiveAmount) + * - State checks (notPaused, onlyOpen) + * - Reentrancy protection + */ diff --git a/src/basics/Gas.sol b/src/basics/Gas.sol new file mode 100644 index 0000000..401f361 --- /dev/null +++ b/src/basics/Gas.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +/// @title Gas Demonstration Contract +/// @author Ishan Lakhwani +/// @notice Demonstrates how gas works in Ethereum +/// @dev Shows an example of an infinite loop that will consume all gas + +/* + * UNDERSTANDING GAS IN ETHEREUM + * + * What is Gas? + * - Gas is like fuel for transactions on Ethereum + * - Every operation (computation) costs some amount of gas + * - Gas is paid in ETH (Ethereum's native currency) + * + * Why do we need Gas? + * - Prevents infinite loops and spam on the network + * - Compensates miners/validators for processing transactions + * - Ensures efficient use of network resources + * + * Gas Costs: + * - Simple operations (like addition) cost less gas + * - Storage operations cost more gas + * - Each transaction has a gas limit to prevent infinite execution + */ +contract Gas { + // State variable that will be incremented + uint256 public i = 0; + + /* + * WARNING: This function is designed to fail! + * + * @notice This function creates an infinite loop that will use up all gas + * @dev When all gas is used: + * - The transaction will fail + * - All changes will be reverted + * - The user will lose their gas fee + * + * Common Gas Terms: + * - Gas Limit: Maximum gas you're willing to use + * - Gas Price: How much ETH you're willing to pay per unit of gas + * - Total Fee = Gas Used ร— Gas Price + */ + function forever() public { + // This loop will run until: + // 1. All gas is consumed + // 2. The transaction hits the gas limit + // 3. The transaction reverts + while (true) { + i += 1; + } + } +} diff --git a/src/basics/HashFunction.sol b/src/basics/HashFunction.sol new file mode 100644 index 0000000..f384035 --- /dev/null +++ b/src/basics/HashFunction.sol @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +/// @title Hash Function Examples +/// @author Ishan Lakhwani +/// @notice Shows how to use keccak256 hash function and avoid collisions + +/* + * UNDERSTANDING HASH FUNCTIONS ๐Ÿ” + * + * Hash functions convert any input into a fixed-size output: + * - One-way function (can't reverse) + * - Same input = Same output + * - Different input = Different output (usually) + * + * Visual Example: + * + * Input Data Hash Function Output (32 bytes) + * "Hello" + 123 โ†’ keccak256() โ†’ 0x8b1a...3f4b + * [๐Ÿ“ฆ Black Box] + * + * Properties: + * +------------------+---------------------------+ + * | Property | Description | + * +------------------+---------------------------+ + * | Deterministic | Same input = Same hash | + * | One-way | Can't reverse the hash | + * | Avalanche | Small change = New hash | + * | Fixed size | Always 32 bytes output | + * +------------------+---------------------------+ + */ +contract HashFunction { + /* + * Hash multiple parameters + * Combines different types into single hash + * + * Flow: + * 1. Pack parameters together (abi.encodePacked) + * 2. Generate hash (keccak256) + * + * Example: + * "Hello" + 123 + 0x... โ†’ 0x8b1a...3f4b + */ + function hash(string memory _text, uint256 _num, address _addr) public pure returns (bytes32) { + // Combine and hash parameters + return keccak256(abi.encodePacked(_text, _num, _addr)); + } + + /* + * Demonstrate hash collision risk + * Shows why careful parameter encoding is important + * + * Problem Case: + * text = "AA", anotherText = "BBB" + * text = "AAB", anotherText = "BB" + * Both produce same packed result: "AABBB" + * + * Visual Example: + * + * "AA" + "BBB" โ†’ "AABBB" โ†’ hash1 + * "AAB" + "BB" โ†’ "AABBB" โ†’ hash1 (Same!) + * + * Solution: Use abi.encode instead of abi.encodePacked + */ + function collision(string memory _text, string memory _anotherText) public pure returns (bytes32) { + // Potentially dangerous with dynamic types + return keccak256(abi.encodePacked(_text, _anotherText)); + } +} + +/* + * HASHING BEST PRACTICES: + * + * 1. Encoding: + * - Use abi.encode for dynamic types + * - Use abi.encodePacked for fixed types + * - Be aware of collision risks + * + * 2. Common Use Cases: + * - Digital signatures + * - Message verification + * - Unique identifiers + * - Commit-reveal schemes + * + * 3. Security: + * - Don't use for passwords directly + * - Consider adding salt for sensitive data + * - Be careful with predictable inputs + * + * Visual Encoding Comparison: + * + * abi.encodePacked: + * "AA" + "BB" โ†’ "AABB" (concatenated) + * + * abi.encode: + * "AA" + "BB" โ†’ 0x...00000AA...0000BB (padded) + */ diff --git a/src/basics/HelloWorld.sol b/src/basics/HelloWorld.sol new file mode 100644 index 0000000..4621c06 --- /dev/null +++ b/src/basics/HelloWorld.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT +// compiler version must be greater than or equal to 0.8.26 and less than 0.9.0 +pragma solidity ^0.8.26; + +contract HelloWorld { + string public greet = "Hello World!"; +} diff --git a/src/basics/IfElse.sol b/src/basics/IfElse.sol new file mode 100644 index 0000000..c13a82f --- /dev/null +++ b/src/basics/IfElse.sol @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +/// @title Control Flow with If-Else Statements +/// @author Ishan Lakhwani +/// @notice Demonstrates conditional statements in Solidity +/// @dev Shows both traditional if-else and ternary operator usage + +/* + * UNDERSTANDING CONTROL FLOW IN SOLIDITY + * + * Control flow statements help us make decisions in our code + * based on certain conditions. They are fundamental to writing + * smart contracts that can handle different scenarios. + */ +contract IfElse { + /* + * @notice Traditional if-else demonstration + * @param x The number to evaluate + * @return uint256 Different values based on the condition: + * - Returns 0 if x < 10 + * - Returns 1 if 10 <= x < 20 + * - Returns 2 if x >= 20 + * + * @dev This function shows how multiple conditions can be chained + * The conditions are evaluated from top to bottom + */ + function foo(uint256 x) public pure returns (uint256) { + // First condition: is x less than 10? + if (x < 10) { + return 0; + } + // Second condition: is x less than 20? + // This only runs if first condition was false + else if (x < 20) { + return 1; + } + // If none of the above conditions were true + // This is the default case + else { + return 2; + } + } + + /* + * @notice Ternary operator demonstration + * @param _x The number to evaluate + * @return uint256 Either 1 or 2 based on the condition + * + * @dev The ternary operator is a shorthand way to write simple if-else statements + * Syntax: condition ? value_if_true : value_if_false + * + * Gas Consideration: + * - Ternary operators can be more gas efficient for simple conditions + * - They make code more concise but should only be used for simple conditions + */ + function ternary(uint256 _x) public pure returns (uint256) { + // Same as: + // if (_x < 10) { + // return 1; + // } else { + // return 2; + // } + return _x < 10 ? 1 : 2; + } +} diff --git a/src/basics/Immutable.sol b/src/basics/Immutable.sol new file mode 100644 index 0000000..887d026 --- /dev/null +++ b/src/basics/Immutable.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +contract Immutable { + address public immutable myAddr; + uint256 public immutable myUint; + + constructor(uint256 _myUint) { + myAddr = msg.sender; // set when the contract is deployed + myUint = _myUint; // canโ€™t change after deployment + } +} diff --git a/src/basics/Interface.sol b/src/basics/Interface.sol new file mode 100644 index 0000000..7975ded --- /dev/null +++ b/src/basics/Interface.sol @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +/// @title Interface Examples in Solidity +/// @author Ishan Lakhwani +/// @notice Shows how interfaces work and how to use them + +/* + * UNDERSTANDING INTERFACES ๐Ÿ”Œ + * + * An interface is a way to interact with external contracts without having their full code + * Think of an interface like a universal remote control: + * - It defines what buttons are available + * - It doesn't know how the TV works internally + * - It just knows how to communicate with it + * + * Visual Example: + * + * Interface (ICounter) Contract (Counter) + * +----------------+ +------------------+ + * | Blueprint ๐Ÿ“‹ | --> | Implementation ๐Ÿ—๏ธ | + * | count() | | count = 0 | + * | increment() | | increment() | + * +----------------+ +------------------+ + * โฌ‡๏ธ โฌ†๏ธ + * MyContract | + * (Uses interface to (Actual contract + * talk to Counter) with real code) + */ + +// The actual contract with implementation +contract Counter { + // State variable to store count + uint256 public count; + + // Function to increase count + function increment() external { + count += 1; + } +} + +/* + * Interface Definition + * Like a blueprint that defines: + * - What functions exist + * - What they return + * - But NOT how they work + */ +interface ICounter { + // Just declares what functions are available + function count() external view returns (uint256); + function increment() external; +} + +/* + * Contract that uses the interface + * Like a universal remote that works with any TV + * that follows the interface pattern + */ +contract MyContract { + /* + * Uses interface to call increment + * + * Flow: + * MyContract -> ICounter -> Counter + * (Remote) (Protocol) (TV) + */ + function incrementCounter(address _counter) external { + // Convert address to ICounter interface + ICounter(_counter).increment(); + } + + /* + * Uses interface to read count + * + * Like checking channel number through remote + */ + function getCount(address _counter) external view returns (uint256) { + return ICounter(_counter).count(); + } +} + +/* + * INTERFACE BEST PRACTICES: + * + * 1. Naming: + * - Prefix with 'I' (ICounter, IERC20) + * - Clear and descriptive + * + * 2. Functions: + * - Must be external + * - No implementation + * - No constructor + * - No state variables + * + * 3. Use Cases: + * - Standardization (ERC20, ERC721) + * - Contract interaction + * - Code organization + * + * Visual Contract Interaction: + * + * Contract A Interface Contract B + * ๐Ÿ“ฑ -> ๐Ÿ”Œ ICounter -> ๐Ÿ“บ + * (Remote) (Protocol) (Device) + */ diff --git a/src/basics/Mapping.sol b/src/basics/Mapping.sol new file mode 100644 index 0000000..6bfdc48 --- /dev/null +++ b/src/basics/Mapping.sol @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +/// @title Mapping Demo +/// @author Ishan Lakhwani +/// @notice Shows how mappings work in Solidity + +/* + * MAPPINGS: Think of them like a dictionary or lookup table + * + * Simple Example: + * Address => Balance + * 0x123... => 100 + * 0x456... => 50 + * 0x789... => 75 + * + * Like a table: + * +-----------+---------+ + * | Address | Balance | + * +-----------+---------+ + * | 0x123... | 100 | + * | 0x456... | 50 | + * | 0x789... | 75 | + * +-----------+---------+ + */ +contract Mapping { + // Simple mapping: connects an address to a number + mapping(address => uint256) public myMap; + + // Read a value + function get(address _addr) public view returns (uint256) { + // If address not found, returns 0 + return myMap[_addr]; + } + + // Save a value + function set(address _addr, uint256 _i) public { + myMap[_addr] = _i; + } + + // Delete a value + function remove(address _addr) public { + delete myMap[_addr]; // Sets value back to 0 + } +} + +/* + * NESTED MAPPINGS: Like a spreadsheet with two keys + * + * Example: User permissions for different token IDs + * + * Visual: + * +------------+-------------+----------+ + * | User | Token ID | Allowed? | + * +------------+-------------+----------+ + * | 0x123... | 1 | true | + * | 0x123... | 2 | false | + * | 0x456... | 1 | false | + * | 0x456... | 2 | true | + * +------------+-------------+----------+ + */ +contract NestedMapping { + // Nested mapping: like a spreadsheet lookup + mapping(address => mapping(uint256 => bool)) public nested; + + // Read permission + function get(address _addr1, uint256 _i) public view returns (bool) { + return nested[_addr1][_i]; + } + + // Set permission + function set(address _addr1, uint256 _i, bool _boo) public { + nested[_addr1][_i] = _boo; + } + + // Remove permission + function remove(address _addr1, uint256 _i) public { + delete nested[_addr1][_i]; // Sets back to false + } +} diff --git a/src/basics/Payable.sol b/src/basics/Payable.sol new file mode 100644 index 0000000..e6a1445 --- /dev/null +++ b/src/basics/Payable.sol @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +/// @title Payable Functions and Ether Handling +/// @author Ishan Lakhwani +/// @notice Shows how to handle ETH transfers in smart contracts + +/* + * UNDERSTANDING PAYABLE ๐Ÿ’ฐ + * + * Think of payable like a digital wallet: + * - Regular functions: Can't receive money + * - Payable functions: Can receive ETH + * + * Visual Flow of ETH: + * + * User/Contract Smart Contract + * [1 ETH] ๐Ÿ’ฐ โ†’ ๐Ÿ“ฆ [Balance] + * payable + * + * Function Types: + * +------------------+-------------------+ + * | Payable | Non-Payable | + * +------------------+-------------------+ + * | Can receive ETH | Can't receive ETH | + * | ๐Ÿ’ฐ Enabled | ๐Ÿšซ Disabled | + * +------------------+-------------------+ + */ +contract Payable { + // Owner can receive ETH (payable address) + address payable public owner; + + /* + * Payable Constructor + * Like opening a new bank account that can receive initial deposit + * + * Flow on Deploy: + * Deployer โ†’ Contract + * [ETH] โ†’ [Balance] + */ + constructor() payable { + owner = payable(msg.sender); + } + + /* + * Deposit Function + * Like a bank deposit slot + * + * Visual: + * User Contract + * [ETH] โ†’ deposit() โ†’ [Balance] + */ + function deposit() public payable {} + + /* + * Non-Payable Function + * Like a regular mailbox - can't accept money + * + * Visual: + * User Contract + * [ETH] โ†’ โŒ notPayable() + */ + function notPayable() public {} + + /* + * Withdraw Function + * Sends all contract balance to owner + * + * Flow: + * Contract Owner + * [Balance] โ†’ withdraw() โ†’ [Owner Balance] + */ + function withdraw() public { + // Get contract's entire balance + uint256 amount = address(this).balance; + + // Send ETH to owner + (bool success,) = owner.call{value: amount}(""); + require(success, "Failed to send Ether"); + } + + /* + * Transfer Function + * Send specific amount to any address + * + * Flow: + * Contract Recipient + * [Balance] โ†’ transfer() โ†’ [Recipient Balance] + * โ†“ -amount โ†‘ +amount + */ + function transfer(address payable _to, uint256 _amount) public { + // Send specified amount + (bool success,) = _to.call{value: _amount}(""); + require(success, "Failed to send Ether"); + } +} + +/* + * PAYABLE BEST PRACTICES: + * + * 1. Security: + * - Always check for successful transfers + * - Use require() after ETH transfers + * - Consider reentrancy attacks + * + * 2. Design: + * - Mark functions payable only if needed + * - Keep track of deposits/withdrawals + * - Consider using events for transfers + * + * 3. Gas Efficiency: + * - Use .call() for transfers (recommended) + * - Avoid .transfer() and .send() (legacy) + * + * Common Patterns: + * - Deposit/Withdraw pattern + * - Pull over Push payments + * - Emergency withdrawal + */ diff --git a/src/basics/Primitives.sol b/src/basics/Primitives.sol new file mode 100644 index 0000000..d9cdc79 --- /dev/null +++ b/src/basics/Primitives.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +/// @title Solidity Primitive Data Types Demo +/// @author Ishan Lakhwani +/// @notice This contract demonstrates the basic primitive data types in Solidity +/// @dev A simple contract to showcase various data types and their usage + +contract Primitives { + // Boolean type - can only be true or false + bool public boo = true; + + /* + * Unsigned Integers (uint) + * uint8 -> ranges from 0 to 2^8-1 (0 to 255) + * uint256 -> ranges from 0 to 2^256-1 + */ + uint8 public u8 = 1; + uint256 public u256 = 456; + uint256 public u = 123; // uint is an alias for uint256 + + /* + * Signed Integers (int) + * int8 -> ranges from -2^7 to 2^7-1 (-128 to 127) + * int256 -> ranges from -2^255 to 2^255-1 + */ + int8 public i8 = -1; + int256 public i256 = 456; + int256 public i = -123; // int is an alias for int256 + + // We can get the minimum and maximum values of int256 + int256 public minInt = type(int256).min; + int256 public maxInt = type(int256).max; + + /* + * Address type + * Holds a 20-byte value (size of an Ethereum address) + * Can be used to store Ethereum addresses and send Ether + */ + address public addr = 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c; + + /* + * Bytes type + * Fixed-size byte arrays + * bytes1 = 1 byte = 8 bits + * bytes2 = 2 bytes = 16 bits + * ... up to bytes32 + */ + bytes1 a = 0xb5; // Single byte (8 bits) stored as hex + bytes1 b = 0x56; // Another single byte + + /* + * Default Values + * Every data type has a default value when not explicitly assigned + * Understanding default values is important for smart contract security + */ + bool public defaultBoo; // false + uint256 public defaultUint; // 0 + int256 public defaultInt; // 0 + address public defaultAddr; // 0x0000000000000000000000000000000000000000 (zero address) +} diff --git a/src/basics/SendEther.sol b/src/basics/SendEther.sol new file mode 100644 index 0000000..5d3045c --- /dev/null +++ b/src/basics/SendEther.sol @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +/// @title Different Ways to Send ETH +/// @author Ishan Lakhwani +/// @notice Shows three different methods to send ETH between contracts + +/* + * UNDERSTANDING ETH TRANSFERS ๐Ÿ’ธ + * + * Three ways to send ETH in Solidity: + * 1. transfer(): Simple but limited + * 2. send(): Low-level but returns bool + * 3. call(): Most flexible and recommended + * + * Visual Comparison: + * +----------------+-------------+----------------+------------------+ + * | Method | Gas Limit | Returns | Recommended? | + * +----------------+-------------+----------------+------------------+ + * | transfer() ๐Ÿ“ฎ | 2300 fixed | throws error | โŒ No (legacy) | + * | send() ๐Ÿ“ซ | 2300 fixed | bool | โŒ No (legacy) | + * | call() ๐Ÿ“ฑ | adjustable | bool & data | โœ… Yes (modern) | + * +----------------+-------------+----------------+------------------+ + */ +contract SendEther { + /* + * Method 1: transfer() + * Like sending a letter with exact postage + * + * Flow: + * Contract A Contract B + * [Balance] โ†’ [Receives ETH] + * transfer() + * + * Limitations: + * - Fixed 2300 gas (might fail with complex receivers) + * - Throws error on failure + */ + function sendViaTransfer(address payable _to) public payable { + // Simple but not recommended + _to.transfer(msg.value); + } + + /* + * Method 2: send() + * Like sending mail with delivery confirmation + * + * Flow: + * Contract A Contract B + * [Balance] โ†’ [Receives ETH] + * send() + * โ†™ + * bool (success) + */ + function sendViaSend(address payable _to) public payable { + // Returns bool for success/failure + bool sent = _to.send(msg.value); + require(sent, "Failed to send Ether"); + } + + /* + * Method 3: call() (Recommended) + * Like a modern digital transfer with confirmation + * + * Flow: + * Contract A Contract B + * [Balance] โ†’ [Receives ETH] + * call() + * โ†™ + * (success, return data) + * + * Advantages: + * - Adjustable gas + * - Returns success bool and data + * - Forward all available gas + */ + function sendViaCall(address payable _to) public payable { + // Modern recommended way + (bool sent, bytes memory data) = _to.call{value: msg.value}(""); + require(sent, "Failed to send Ether"); + } +} + +/* + * BEST PRACTICES FOR SENDING ETH: + * + * 1. Security First: + * - Always use call() for ETH transfers + * - Check return values + * - Consider reentrancy risks + * + * 2. Gas Considerations: + * - transfer() and send() have 2300 gas limit + * - call() allows flexible gas limits + * - More complex receivers need more gas + * + * Visual Gas Comparison: + * + * transfer()/send() [===========] 2300 gas fixed + * call() [==============================] flexible + * + * Common Patterns: + * - Check-Effects-Interactions + * - Use reentrancy guards + * - Always verify success + */ diff --git a/src/basics/SimpleStorage.sol b/src/basics/SimpleStorage.sol new file mode 100644 index 0000000..f9d4a4b --- /dev/null +++ b/src/basics/SimpleStorage.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +/// @title Simple Storage Contract Demo +/// @author Ishan Lakhwani +/// @notice A basic example of reading and writing state in a smart contract +/// @dev Demonstrates the fundamental concepts of state variables and functions + +contract SimpleStorage { + /* + * State Variable Declaration + * - uint256 is an unsigned integer that can store values from 0 to 2^256-1 + * - 'public' automatically creates a getter function + * - This variable is stored permanently on the blockchain + */ + uint256 public num; + + /* + * Write Function + * @param _num The number to store in the contract + * @notice Updates the state variable 'num' + * @dev This function: + * - Requires a transaction (costs gas) + * - Changes the blockchain state + * - Can be called by any address + */ + function set(uint256 _num) public { + num = _num; + } + + /* + * Read Function + * @notice Retrieves the stored number + * @return The current value of 'num' + * @dev This function: + * - Is marked as 'view' (no state modification) + * - Doesn't cost gas when called externally + * - Can be called without sending a transaction + */ + function get() public view returns (uint256) { + return num; + } +} diff --git a/src/basics/StructTodos.sol b/src/basics/StructTodos.sol new file mode 100644 index 0000000..c8c4688 --- /dev/null +++ b/src/basics/StructTodos.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +contract Todos { + // define a struct called 'Todo' + struct Todo { + string text; + bool completed; + } + + // array of 'Todo' structs + Todo[] public todos; + + function create(string calldata _text) public { + // 3 ways to create a new todo: + // (1) direct initialization + todos.push(Todo(_text, false)); + // (2) named arguments + todos.push(Todo({text: _text, completed: false})); + // (3) create an empty struct in memory, then set values + Todo memory todo; + todo.text = _text; + // 'completed' defaults to false + + todos.push(todo); + } + + function get(uint256 _index) public view returns (string memory text, bool completed) { + Todo storage todo = todos[_index]; + return (todo.text, todo.completed); + } + + function updateText(uint256 _index, string calldata _text) public { + Todo storage todo = todos[_index]; + todo.text = _text; + } + + function toggleCompleted(uint256 _index) public { + Todo storage todo = todos[_index]; + todo.completed = !todo.completed; + } +} diff --git a/src/basics/TransientStorage.sol b/src/basics/TransientStorage.sol new file mode 100644 index 0000000..758b3bd --- /dev/null +++ b/src/basics/TransientStorage.sol @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +/// @title Transient Storage Demo +/// @author Ishan Lakhwani +/// @notice Shows how to use transient storage (new in Solidity) + +/* + * UNDERSTANDING TRANSIENT STORAGE + * + * What is Transient Storage? ๐Ÿ”„ + * - Like a temporary notepad that gets erased after each transaction + * - Cheaper than regular storage + * - Only lasts during the current transaction + * - Useful for temporary calculations + * + * Visual Comparison: + * +-------------------+-------------------------+------------------------+ + * | Storage | Regular Storage | Transient Storage | + * | Type | ๐Ÿ’พ | ๐Ÿ”„ | + * +-------------------+-------------------------+------------------------+ + * | Lasts Until | Forever (until changed) | End of transaction | + * | Gas Cost | Expensive | Cheaper | + * | Persists Between | Yes | No | + * | Transactions | | | + * +-------------------+-------------------------+------------------------+ + * + * Real World Analogy: + * - Regular Storage = Writing in a permanent ledger + * - Transient Storage = Using a sticky note that you throw away after + */ +contract TestTransientStorage { + // Define a constant slot for transient storage + // Think of this like choosing which sticky note to use + bytes32 constant SLOT = 0; + + /* + * @notice Test function to demonstrate transient storage + * @dev Uses assembly to access transient storage + * + * Flow: + * 1. Write value to transient storage + * 2. Make external call + * 3. Value is gone after call returns + */ + function test() public { + // Using inline assembly to access transient storage + assembly { + // Store value 321 in transient storage + // Like writing "321" on our sticky note + tstore(SLOT, 321) + } + + // Create empty bytes array for the call + bytes memory b = ""; + + // Make an external call + // This will clear our transient storage + // Like crumpling up our sticky note + msg.sender.call(b); + } + + /* + * @notice Read the current value from transient storage + * @return v The value in transient storage (0 if cleared) + * @dev Uses assembly to read transient storage + */ + function val() public view returns (uint256 v) { + // Read from transient storage + // Like checking what's written on our sticky note + assembly { + v := tload(SLOT) + } + } +} + +/* + * IMPORTANT NOTES: + * + * 1. Transient storage is cleared: + * - After external calls + * - At the end of transaction + * Like throwing away a sticky note + * + * 2. Use cases: + * - Temporary calculations + * - Passing values between functions + * - Gas optimization + * + * 3. Advantages: + * - Cheaper than regular storage + * - Useful for temporary data + * - Good for gas optimization + * + * 4. Limitations: + * - Only lasts during transaction + * - Cleared after external calls + * - Requires assembly code + */ diff --git a/src/basics/Variables.sol b/src/basics/Variables.sol new file mode 100644 index 0000000..f34a255 --- /dev/null +++ b/src/basics/Variables.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +/// @title Solidity Variables Demo +/// @author Ishan Lakhwani +/// @notice Demonstrates the three types of variables in Solidity: State, Local, and Global +/// @dev Shows basic usage of different variable scopes and their characteristics + +contract Variables { + /* + * State Variables + * - Permanently stored in contract storage + * - Expensive to use (costs gas) + * - Accessible by all contract functions + * - Persists between function calls + * - Value is stored on the blockchain + */ + string public text = "Hello"; + uint256 public num = 123; + + /* + * This function demonstrates the usage of both local and global variables + * The 'view' keyword means this function doesn't modify state + */ + function doSomething() public view { + /* + * Local Variables + * - Temporary variables that only exist during function execution + * - Not stored on the blockchain + * - Gas efficient as they only exist in memory + * - Cannot be accessed outside this function + */ + uint256 i = 456; + + /* + * Global Variables + * Special variables provided by Solidity + * Give access to blockchain properties and transaction context + * Some common global variables include: + * - block.timestamp: Current block's timestamp + * - msg.sender: Address of the account calling the function + * - block.number: Current block number + * - msg.value: Amount of ETH sent with the transaction + * - block.difficulty: Current block's difficulty + */ + uint256 timestamp = block.timestamp; // Unix timestamp of current block + address sender = msg.sender; // Address of the account/contract calling this function + + // Note: These variables are declared but not used in this function + // In a real contract, you would typically do something with these values + } +} diff --git a/src/basics/ViewAndPure.sol b/src/basics/ViewAndPure.sol new file mode 100644 index 0000000..69a7ed1 --- /dev/null +++ b/src/basics/ViewAndPure.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +/// @title View and Pure Functions Demo +/// @author Ishan Lakhwani +/// @notice Shows how view and pure functions work + +/* + * FUNCTION TYPES IN SOLIDITY: + * + * 1. Regular Function: Can modify state + * setNumber(5) => Changes state โœ๏ธ + * + * 2. View Function: Can read but not modify + * getNumber() => Just looks at state ๐Ÿ‘€ + * + * 3. Pure Function: Cannot read or modify + * add(2,3) => Just computes something ๐Ÿงฎ + * + * Visual Example: + * +-------------------+----------------+----------------+ + * | | Reads State? | Changes State? | + * +-------------------+----------------+----------------+ + * | Regular Function | โœ… | โœ… | + * | View Function | โœ… | โŒ | + * | Pure Function | โŒ | โŒ | + * +-------------------+----------------+----------------+ + */ +contract ViewAndPure { + // This is a state variable + uint256 public x = 1; + + // View function: can look at state but not change it + // Like checking your wallet balance + function addToX(uint256 y) public view returns (uint256) { + return x + y; // reads x but doesn't change it + } + + // Pure function: just does computation + // Like using a calculator + function add(uint256 i, uint256 j) public pure returns (uint256) { + return i + j; // doesn't touch any state + } +} diff --git a/src/basics/Visibility.sol b/src/basics/Visibility.sol new file mode 100644 index 0000000..f0fbafb --- /dev/null +++ b/src/basics/Visibility.sol @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +/// @title Function Visibility Demo +/// @author Ishan Lakhwani +/// @notice Shows different types of function visibility in Solidity + +/* + * UNDERSTANDING FUNCTION VISIBILITY ๐Ÿ”’ + * + * Think of visibility like different levels of access in a building: + * + * 1. private ๐Ÿ”’ : Only accessible within this room + * 2. internal ๐Ÿ  : Accessible within this building and connected buildings + * 3. public ๐ŸŒ : Anyone can access from anywhere + * 4. external ๐Ÿšช : Only accessible from outside the building + * + * Visual Example: + * +----------------+-------------------+--------------------+------------------+ + * | | Same Contract | Child Contract | Other Contracts | + * +----------------+-------------------+--------------------+------------------+ + * | private ๐Ÿ”’ | โœ… | โŒ | โŒ | + * | internal ๐Ÿ  | โœ… | โœ… | โŒ | + * | public ๐ŸŒ | โœ… | โœ… | โœ… | + * | external ๐Ÿšช | โŒ | โŒ | โœ… | + * +----------------+-------------------+--------------------+------------------+ + */ +contract Base { + /* + * PRIVATE FUNCTION ๐Ÿ”’ + * - Like your personal diary + * - Only this contract can read it + * - Most restrictive access + */ + function privateFunc() private pure returns (string memory) { + return "private function called"; + } + + // Function to test private access + function testPrivateFunc() public pure returns (string memory) { + // We can call privateFunc() here because we're in the same contract + return privateFunc(); + } + + /* + * INTERNAL FUNCTION ๐Ÿ  + * - Like a family secret + * - This contract and child contracts can use it + * - Good for shared functionality + */ + function internalFunc() internal pure returns (string memory) { + return "internal function called"; + } + + function testInternalFunc() public pure virtual returns (string memory) { + return internalFunc(); + } + + /* + * PUBLIC FUNCTION ๐ŸŒ + * - Like a public park + * - Anyone can use it + * - Most open access + */ + function publicFunc() public pure returns (string memory) { + return "public function called"; + } + + /* + * EXTERNAL FUNCTION ๐Ÿšช + * - Like a store's entrance + * - Only accessible from outside + * - Cannot be called from inside this contract + */ + function externalFunc() external pure returns (string memory) { + return "external function called"; + } + + /* + * VARIABLE VISIBILITY + * Variables can also have visibility: + */ + string private privateVar = "my private variable"; // Like a personal item + string internal internalVar = "my internal variable"; // Like family property + string public publicVar = "my public variable"; // Like a public notice + // Note: Variables cannot be external +} + +/* + * CHILD CONTRACT EXAMPLE + * Shows how inheritance affects visibility + */ +contract Child is Base { + // Child contract can use internal functions + // Like a child knowing family secrets + function testInternalFunc() public pure override returns (string memory) { + return internalFunc(); + } + + // Note: Child cannot access private functions + // This would not work: + // function testPrivateFunc() public pure returns (string memory) { + // return privateFunc(); // โŒ Error! + // } +} + +/* + * VISIBILITY BEST PRACTICES: + * + * 1. Start with most restrictive (private) + * 2. Only make functions public/external if needed + * 3. Use internal for shared contract logic + * 4. Use external for interface functions + * + * Think of it like security clearance: + * - Give minimum access needed + * - Be explicit about who can access what + * - Document why certain visibility is chosen + */ diff --git a/src/basics/getAjob.txt b/src/basics/getAjob.txt new file mode 100644 index 0000000..ef7b670 --- /dev/null +++ b/src/basics/getAjob.txt @@ -0,0 +1,219 @@ +BLOCKCHAIN DEVELOPER CAREER GUIDE ๐Ÿš€ +=================================== + +1. LEARNING ROADMAP ๐Ÿ“š +--------------------- + +Basic Prerequisites: +- Strong programming fundamentals +- JavaScript/TypeScript +- React/Node.js +- Basic cryptography understanding +- Web development basics (HTML, CSS, APIs) + +Core Blockchain Skills: +1. Solidity + - Smart contracts + - Gas optimization + - Security best practices + - Testing frameworks (Hardhat, Foundry) + +2. Web3 Development + - ethers.js/web3.js + - Smart contract interaction + - Wallet integration + - DApp development + +3. Blockchain Concepts + - Consensus mechanisms + - Layer 1/Layer 2 solutions + - DeFi protocols + - Token standards (ERC20, ERC721, etc.) + +Advanced Skills: +- ZK proofs +- MEV +- Cross-chain bridges +- L2 scaling solutions +- Formal verification + +2. LEARNING RESOURCES ๐Ÿ“– +----------------------- + +Free Resources: +- CryptoZombies (interactive Solidity) +- Ethereum.org documentation +- OpenZeppelin docs & tutorials +- Solidity by Example +- LearnWeb3.io +- Buildspace.so + +Paid Courses: +- Udemy Blockchain courses +- Coursera Blockchain specialization +- Alchemy University +- Encode Club bootcamps +- ConsenSys Academy + +Practice Projects: +1. Basic: + - Token contracts + - NFT collections + - Simple DeFi protocols + +2. Advanced: + - DEX implementation + - Lending protocols + - DAO frameworks + +3. JOB HUNTING PLATFORMS ๐Ÿ’ผ +-------------------------- + +Crypto-Specific: +1. Web3.career +2. CryptoJobsList +3. Cryptocurrency Jobs +4. Web3Jobs.com +5. UseWeb3.xyz/jobs + +General Platforms with Web3 Jobs: +1. LinkedIn +2. AngelList +3. Indeed +4. Glassdoor +5. Stack Overflow Jobs + +Direct Company Websites: +- Consensys +- Alchemy +- OpenZeppelin +- Chainlink +- Polygon +- Ethereum Foundation + +4. NETWORKING & COMMUNITY ๐Ÿค +--------------------------- + +Online Communities: +1. Discord servers: + - Ethereum + - OpenZeppelin + - DeFi protocols + - NFT projects + +2. Twitter: + - Follow blockchain developers + - Engage in technical discussions + - Share your projects + +3. GitHub: + - Contribute to open source + - Share your code + - Review others' code + +Events & Conferences: +- ETHGlobal hackathons +- DevCon +- ETHDenver +- Local blockchain meetups + +5. PORTFOLIO BUILDING ๐Ÿ—๏ธ +------------------------ + +Essential Components: +1. GitHub Profile: + - Clean, documented code + - Variety of projects + - Regular contributions + +2. Personal Website: + - Project showcases + - Technical blog posts + - Clear documentation + +3. Sample Projects: + - DeFi applications + - NFT platforms + - DAO tools + - Developer tools + +6. INTERVIEW PREPARATION ๐Ÿ“ +-------------------------- + +Technical Topics: +1. Solidity: + - Gas optimization + - Security vulnerabilities + - Design patterns + - Testing strategies + +2. Blockchain: + - Consensus mechanisms + - Network architecture + - Scaling solutions + - Protocol design + +3. Web3: + - Frontend integration + - Wallet connections + - Transaction handling + - State management + +Practice Resources: +- Smart contract auditing +- CTF challenges +- Code reviews +- Mock interviews + +7. SALARY EXPECTATIONS ๐Ÿ’ฐ +------------------------ + +Entry Level: +- Junior: $70-100k +- Smart Contract Dev: $80-120k +- DApp Developer: $75-110k + +Mid Level (2-4 years): +- Senior: $120-180k +- Lead Developer: $150-200k +- Protocol Engineer: $130-190k + +Senior Level (5+ years): +- Architect: $180-250k+ +- Head of Blockchain: $200k+ +- Protocol Lead: $200k+ + +Note: Salaries vary by location, company size, and expertise + +8. GROWTH PATHS ๐Ÿ“ˆ +----------------- + +Specializations: +1. Security: + - Smart contract auditor + - Security researcher + - Bug bounty hunter + +2. Protocol Development: + - DeFi architect + - L2 developer + - Cross-chain specialist + +3. Infrastructure: + - Node operator + - Network developer + - Tools developer + +4. Research: + - ZK researcher + - Cryptography specialist + - Protocol researcher + +Remember: +- Stay updated with latest developments +- Build in public +- Network actively +- Contribute to open source +- Focus on fundamentals first +- Practice security best practices +- Join hackathons and competitions diff --git a/src/rounding-error/StakingRewards.sol b/src/rounding-error/StakingRewards.sol new file mode 100644 index 0000000..c8ce646 --- /dev/null +++ b/src/rounding-error/StakingRewards.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/// @title StakingRewards - A vulnerable staking contract +/// @author Ishan Lakhwani +/// @notice This contract demonstrates rounding errors in Solidity +/// @dev This contract is intentionally vulnerable to rounding errors for educational purposes + +contract StakingRewards { + mapping(address => uint256) public stakedBalances; + uint256 public totalStaked; + uint256 public rewardsPerBlock; + uint256 public lastBlockRewarded; + + constructor(uint256 _rewardsPerBlock) { + rewardsPerBlock = _rewardsPerBlock; + lastBlockRewarded = block.number; + } + + function stake(uint256 _amount) public { + require(_amount > 0, "Amount must be positive"); + stakedBalances[msg.sender] += _amount; + totalStaked += _amount; + } + + function getReward() public { + uint256 blocksElapsed = block.number - lastBlockRewarded; + if (blocksElapsed > 0 && totalStaked > 0) { + uint256 totalRewards = rewardsPerBlock * blocksElapsed; + + // Vulnerable calculation - division happens for each user separately + // causing rounding errors to accumulate + uint256 share = stakedBalances[msg.sender] / totalStaked; // This division loses precision + uint256 rewardAmount = totalRewards * share; // Multiplication after division amplifies the error + + stakedBalances[msg.sender] += rewardAmount; + lastBlockRewarded = block.number; + } + } +} diff --git a/src/rounding-error/VulnerableVault.sol b/src/rounding-error/VulnerableVault.sol new file mode 100644 index 0000000..bc3b8e7 --- /dev/null +++ b/src/rounding-error/VulnerableVault.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/// @title VulnerableVault - A vault contract with rounding error vulnerability +/// @author Ishan Lakhwani +/// @notice This contract demonstrates how rounding errors can be exploited +/// @dev The withdraw function contains a rounding error vulnerability in fee calculation + +contract VulnerableVault { + mapping(address => uint256) public balances; + uint256 public totalDeposits; + + event Deposit(address indexed user, uint256 amount); + event Withdraw(address indexed user, uint256 amount); + + function deposit() public payable { + require(msg.value > 0, "Amount must be positive"); + balances[msg.sender] += msg.value; + totalDeposits += msg.value; + emit Deposit(msg.sender, msg.value); + } + + function withdraw(uint256 _amount) public { + require(_amount > 0, "Amount must be positive"); + require(balances[msg.sender] >= _amount, "Insufficient balance"); + + // Vulnerable: Multiple rounding operations create cumulative errors + uint256 feePercentage = 3; // 0.3% + uint256 withdrawalFee = (_amount * feePercentage) / 1000; + + // Additional rounding error in fee calculation + uint256 complexFee = (withdrawalFee * _amount) / totalDeposits; + uint256 finalFee = (withdrawalFee + complexFee) / 2; + + uint256 amountAfterFee = _amount - finalFee; + + balances[msg.sender] -= _amount; + totalDeposits -= _amount; + + (bool success,) = payable(msg.sender).call{value: amountAfterFee}(""); + require(success, "Transfer failed"); + + emit Withdraw(msg.sender, _amount); + } +} diff --git a/test/RoundingErrorTest.t.sol b/test/RoundingErrorTest.t.sol new file mode 100644 index 0000000..d47531f --- /dev/null +++ b/test/RoundingErrorTest.t.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "forge-std/Test.sol"; +import "../src/rounding-error/StakingRewards.sol"; + +contract StakingRewardsTest is Test { + StakingRewards public rewards; + address payable user1 = payable(address(0x1)); + address payable user2 = payable(address(0x2)); + address payable attacker = payable(address(0x1337)); + + function setUp() public { + rewards = new StakingRewards(100); // 100 rewards per block + } + + function testRoundingExploit() public { + vm.prank(user1); + rewards.stake(3); + vm.prank(user2); + rewards.stake(7); + + vm.roll(block.number + 10); + + vm.prank(user1); + rewards.getReward(); + vm.prank(user2); + rewards.getReward(); + + uint256 user1BalanceAfterFirstReward = rewards.stakedBalances(user1); + uint256 user2BalanceAfterFirstReward = rewards.stakedBalances(user2); + + vm.prank(attacker); + rewards.stake(1); + vm.roll(block.number + 10); + vm.prank(attacker); + rewards.getReward(); + + console.log("Initial stakes - User1: 3, User2: 7, Attacker: 1"); + console.log("After rewards:"); + console.log("User1 balance:", user1BalanceAfterFirstReward); + console.log("User2 balance:", user2BalanceAfterFirstReward); + console.log("Attacker balance:", rewards.stakedBalances(attacker)); + + // Due to rounding errors, attacker should get disproportionate rewards + assertGt(rewards.stakedBalances(attacker), 1); + } + + function testNoExploit() public { + vm.prank(user1); + rewards.stake(300); + vm.prank(user2); + rewards.stake(700); + + vm.roll(block.number + 10); + + vm.prank(user1); + rewards.getReward(); + vm.prank(user2); + rewards.getReward(); + + uint256 user1BalanceAfterFirstReward = rewards.stakedBalances(user1); + uint256 user2BalanceAfterFirstReward = rewards.stakedBalances(user2); + + console.log("Initial stakes - User1: 300, User2: 700"); + console.log("After rewards:"); + console.log("User1 balance:", user1BalanceAfterFirstReward); + console.log("User2 balance:", user2BalanceAfterFirstReward); + + // With larger numbers, rounding errors still occur but are less significant + assertGt(user1BalanceAfterFirstReward, 300); + assertGt(user2BalanceAfterFirstReward, 700); + } +}