Skip to content

Commit

Permalink
solidity basics notes added
Browse files Browse the repository at this point in the history
  • Loading branch information
bluntbrain committed Jan 17, 2025
1 parent 5e417a7 commit 41e41ad
Show file tree
Hide file tree
Showing 36 changed files with 2,879 additions and 0 deletions.
107 changes: 107 additions & 0 deletions src/basics/AbiDecode.sol
Original file line number Diff line number Diff line change
@@ -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[]) ❌
*/
123 changes: 123 additions & 0 deletions src/basics/AbiEncode.sol
Original file line number Diff line number Diff line change
@@ -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
*/
74 changes: 74 additions & 0 deletions src/basics/Array.sol
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading

0 comments on commit 41e41ad

Please sign in to comment.