### Implement Yul Control Flow Source: https://github.com/andreitoma8/learn-yul/blob/master/README.md Provides examples of for-loops, if-statements, and switch-statements in Yul, highlighting the lack of a native boolean type where non-zero values represent true. ```solidity function forLoop(uint256 n) public { assembly { for {let i := 0} lt(i, n) {i := add(i, 1)} { // do something } } } function ifTrue(uint256 n) public { assembly { if 2 { // 2 is true // do something } } } function switchStatement(uint256 n) public { assembly { switch n case 0 { /* if n == 0 */ } case 1 { /* if n == 1 */ } default { /* default case */ } } } ``` -------------------------------- ### Memory Manipulation Opcodes Source: https://github.com/andreitoma8/learn-yul/blob/master/README.md Examples of using mstore and mstore8 to write data to memory slots. ```solidity assembly { mstore(0, 7) } ``` ```solidity assembly { mstore8(0, 7) } ``` -------------------------------- ### Yul: Handle Return Data Size and Copying Source: https://github.com/andreitoma8/learn-yul/blob/master/README.md Explains how to use Yul's `returndatasize` and `returndatacopy` to retrieve data returned from a previous call. `returndatasize` gets the size of the return data, and `returndatacopy` copies it from the return buffer to memory for further processing or return. ```solidity contract A { // Function that returns bytes of length len function getBytes(uint256 len) external pure returns (bytes memory result) { result = new bytes(len); for (uint256 i; i < len; i ++) { result[i] = 0xab; } } } contract B { function callGetBytes(address _a) external view returns (bytes memory) { assembly { // load the free memory pointer let freeMemPointer := mload(0x40) // store the function selector of getBytes(uint256) in memory mstore(freeMemPointer, 0x57bc2ef3) // store the argument 10 for getBytes(uint256) in the next memory slot mstore(add(freeMemPointer, 0x20), 10) // update the free memory pointer mstore(0x40, add(freeMemPointer, 0x40)) // call the getBytes function of contract A and don't store the result if iszero(staticcall(gas(), _a, add(freeMemPointer, 28), 36, 0x00, 0x00)) { revert(0,0) } // store the return data in memory starting from the free memory pointer returndatacopy(mload(0x40), 0 , returndatasize()) // return the result from memory return(mload(0x40), returndatasize()) } } } ``` -------------------------------- ### Emitting Events with Log Functions in Yul Source: https://github.com/andreitoma8/learn-yul/blob/master/README.md Yul provides `log0` through `log4` functions to emit events. These functions take a memory pointer 'p', data size 's', and up to four topics (t1-t4). Topics include the event signature hash and indexed arguments. The data payload starts at memory slot 'p' and has size 's'. ```solidity event SomeLog(uint256 indexed a, uint256 indexed b, bool c); function f() external { assembly { // keccak256("SomeLog(uint256,uint256)") let signature := 0xc200138117cf199dd335a2c6079a6e1be01e6592b6a76d4b5fc31b169df819cc // store 1 in memory slot 0x80 mstore(0x80, 1) // emit the event SomeLog(2, 3, true) log3(0x80, 0x20, signature, 2, 3) } } ``` -------------------------------- ### Return Data from Memory in Yul Source: https://github.com/andreitoma8/learn-yul/blob/master/README.md The `return(a,b)` function in Yul retrieves data of size 'b' starting from memory slot 'a'. This is useful for returning data larger than 32 bytes. Note that if the returned data is smaller than expected by the client, decoding may fail, but if it's larger, the client will read the expected number of bytes. ```solidity function f() external returns (uint256, uint256) { assembly { // store 1 and 2 in memory slots 0x80 and 0xa0 mstore(0x80, 1) mstore(0xa0, 2) // return the data from slot 0x80 while 0x40 being the size of the return data return(0x80, 0x40) } } ``` -------------------------------- ### Access Storage Arrays and Mappings Source: https://context7.com/andreitoma8/learn-yul/llms.txt Illustrates how to calculate storage locations for fixed arrays, dynamic arrays, and nested mappings using keccak256 hashing. ```solidity contract ArrayMappingExample { uint256[5] fixedArr; uint256[] dynamicArr; mapping(uint256 => uint256) simpleMap; mapping(uint256 => mapping(uint256 => uint256)) nestedMap; function getFixedArray(uint256 index) public view returns (uint256 value) { assembly { value := sload(add(fixedArr.slot, index)) } } function getDynamicArray(uint256 index) public view returns (uint256 value) { uint256 slot; assembly { slot := dynamicArr.slot } bytes32 location = keccak256(abi.encode(slot)); assembly { value := sload(add(location, index)) } } function getMapping(uint256 key) public view returns (uint256 value) { bytes32 slot; assembly { slot := simpleMap.slot } bytes32 location = keccak256(abi.encode(key, uint256(slot))); assembly { value := sload(location) } } function getNestedMapping(uint256 key1, uint256 key2) public view returns (uint256 value) { bytes32 slot; assembly { slot := nestedMap.slot } bytes32 location = keccak256(abi.encode(key2, keccak256(abi.encode(key1, uint256(slot))))); assembly { value := sload(location) } } } ``` -------------------------------- ### Define Yul Functions and Variables Source: https://github.com/andreitoma8/learn-yul/blob/master/README.md Shows the syntax for declaring Yul functions with return values and initializing variables using the let keyword. ```yul function functionName(param1, param2, ...) -> return1, return2, ... { // code } let x := 1 ``` -------------------------------- ### Handling Dynamic Arrays in Assembly Source: https://github.com/andreitoma8/learn-yul/blob/master/README.md Explains the memory layout of dynamic arrays, where the first slot stores the length, and demonstrates manual initialization of uninitialized dynamic arrays to avoid memory collisions. ```solidity function f(uint256[] memory arr) external { assembly { let location := arr let length := arr.length mload(add(location, 0x20)) // first element } } function initDynamic() external returns(bytes memory) { bytes memory b; assembly { b := mload(0x40) mstore(b, 3) mstore(add(b, 0x20), 0xffffff0000000000000000000000000000000000000000000000000000000000) mstore(0x40, add(b, 0x40)) } return b; } ``` -------------------------------- ### Comparing abi.encode and abi.encodePacked Source: https://github.com/andreitoma8/learn-yul/blob/master/README.md Illustrates the memory layout differences between abi.encode (padded to 32 bytes) and abi.encodePacked (no padding) when accessed via assembly. ```solidity // abi.encode abi.encode(uint256(1), uint256(2)); // abi.encodePacked abi.encodePacked(uint256(1), uint128(2)); ``` -------------------------------- ### Perform Storage Operations with sload and sstore Source: https://context7.com/andreitoma8/learn-yul/llms.txt Demonstrates how to read and write to contract storage slots using Yul. Includes handling for packed variables by calculating offsets and bitwise shifts. ```solidity contract StorageExample { uint256 x; // slot 0 uint256 y; // slot 1 function setX(uint256 _x) public { assembly { sstore(x.slot, _x) // Store value at x's storage slot } } function getX() public view returns (uint256 result) { assembly { result := sload(x.slot) // Load value from x's storage slot } } uint128 a; // slot 2, offset 0 uint128 b; // slot 2, offset 16 bytes function getPackedB() public view returns (uint128 result) { assembly { let wholeSlot := sload(b.slot) let shifted := shr(mul(b.offset, 8), wholeSlot) result := and(shifted, 0xffffffffffffffffffffffffffffffff) } } } ``` -------------------------------- ### Access Mapping Values via Assembly Source: https://github.com/andreitoma8/learn-yul/blob/master/README.md Shows how to retrieve values from simple and nested mappings by concatenating keys and storage slots before hashing. ```solidity mapping(uint256 => uint256) map; function get(uint256 key) public view returns (uint256 value) { bytes32 slot; assembly { slot := map.slot } bytes32 location = keccak256(abi.encode(key, uint256(slot))); assembly{ value := sload(location) } } ``` ```solidity mapping(uint256 => mapping(uint256 => uint256)) map; function get(uint256 key1, uint256 key2) public view returns (uint256 value) { bytes32 slot; assembly { slot := map.slot } bytes32 location = keccak256(abi.encode(key2, keccak256(abi.encode(key1, uint256(slot))))); assembly{ value := sload(location) } } ``` -------------------------------- ### Retrieve Free Memory Pointer Source: https://github.com/andreitoma8/learn-yul/blob/master/README.md Demonstrates how to read the free memory pointer from slot 0x40 to safely allocate new memory. ```solidity assembly { let freeMemoryPointer := mload(0x40) } ``` -------------------------------- ### Solidity: Crowdfunding Contract Implementation Source: https://github.com/andreitoma8/learn-yul/blob/master/CrowdFunding-Contract-Example.md This snippet shows the full implementation of a Solidity smart contract for a crowdfunding platform. It defines structures for campaigns and mappings to store campaign data and user donations. Key functions include creating campaigns, accepting contributions, and handling withdrawals for both owners and donors. ```Solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract CrowdFundingSolidity { uint256 id; struct Campaign { address owner; uint256 targetAmount; uint256 endTimestamp; uint256 amountRaised; } mapping(uint256 => Campaign) campaigns; mapping(address => uint256) donations; function createCampaign(uint256 target, uint256 duration) public { campaigns[id] = Campaign({ owner: msg.sender, targetAmount: target, endTimestamp: block.timestamp + duration, amountRaised: 0 }); id++; } function contribute(uint256 campaignId) public payable { require(msg.value > 0); require(block.timestamp < campaigns[campaignId].endTimestamp); donations[msg.sender] += msg.value; campaigns[campaignId].amountRaised += msg.value; } function withdrawOwner(uint256 campaignId) public { Campaign memory campaign = campaigns[campaignId]; require(msg.sender == campaign.owner); require(campaign.amountRaised > campaign. targetAmount); campaigns[campaignId].amountRaised = 0; (bool os, ) = payable(msg.sender).call{value: campaign.amountRaised}(""); require(os); } function withdrawDonor(uint256 campaignId) public { require(donations[msg.sender] > 0); Campaign memory campaign = campaigns[campaignId]; require(block.timestamp > campaign.endTimestamp); require(campaign.targetAmount < campaign.amountRaised); uint256 amountDonated = donations[msg.sender]; donations[msg.sender] = 0; (bool os, ) = payable(msg.sender).call{value: amountDonated}(""); require(os); } } ``` -------------------------------- ### Control Execution with Return and Revert Source: https://context7.com/andreitoma8/learn-yul/llms.txt Demonstrates how to manually return data from memory or trigger a transaction revert using Yul assembly. ```solidity function returnMultiple() external pure returns (uint256, uint256) { assembly { mstore(0x80, 1) mstore(0xa0, 2) return(0x80, 0x40) } } function conditionalRevert(uint256 x) external pure { assembly { if iszero(x) { revert(0, 0) } } } function keccakHash() external pure returns (bytes32) { assembly { mstore(0x80, 1) mstore(0xa0, 2) mstore(0xc0, keccak256(0x80, 0x40)) return(0xc0, 0x20) } } ``` -------------------------------- ### Define Inline Assembly in Solidity Source: https://github.com/andreitoma8/learn-yul/blob/master/README.md Demonstrates the basic syntax for embedding Yul code within a Solidity contract using the assembly keyword. ```solidity contract C { function f() public { assembly { // Yul code goes here } } } ``` -------------------------------- ### Implement Proxy Delegation in Yul Source: https://github.com/andreitoma8/learn-yul/blob/master/README.md Demonstrates how to forward calldata to an implementation contract using delegatecall. It manages memory manually to handle input and output data buffers. ```Yul calldatacopy(0, 0, calldatasize()) let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) returndatacopy(0, 0, returndatasize()) switch result case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } ``` -------------------------------- ### Accessing Memory Structs and Fixed Arrays Source: https://github.com/andreitoma8/learn-yul/blob/master/README.md Shows how structs and fixed-size arrays are laid out in memory and accessed via assembly using their memory offsets. ```solidity struct S { uint256 a; uint256 b; } function f() external { S memory s = S(a: 1, b: 2); assembly { let freeMemoryPointer := mload(0x40) mload(0x80) // returns a (1) mload(0xa0) // returns b (2) } } ``` -------------------------------- ### Manage Memory Operations with mload and mstore Source: https://context7.com/andreitoma8/learn-yul/llms.txt Shows how to interact with the free memory pointer to allocate and read data in EVM memory. Emphasizes the importance of updating the free memory pointer to avoid data corruption. ```solidity function memoryOps() public pure returns (uint256, uint256) { uint256 val1; uint256 val2; assembly { let freeMemPtr := mload(0x40) mstore(freeMemPtr, 42) mstore(add(freeMemPtr, 0x20), 100) val1 := mload(freeMemPtr) val2 := mload(add(freeMemPtr, 0x20)) mstore(0x40, add(freeMemPtr, 0x40)) mstore8(freeMemPtr, 0x07) } return (val1, val2); } ``` -------------------------------- ### Yul Inline Assembly in Solidity Source: https://context7.com/andreitoma8/learn-yul/llms.txt Demonstrates how to use Yul code within Solidity smart contracts using the `assembly` keyword. It shows basic assignments of boolean, uint256, and bytes32 types. Note that non-zero values represent true in Yul. ```solidity contract YulExample { function f() public pure returns (bool, uint256, bytes32) { bool x; uint256 y; bytes32 z; assembly { x := 1 // boolean true (any non-zero value) y := 0xa // hexadecimal 10 z := "Hello World!" // bytes32 string literal } return(x, y, z); // Returns: true, 10, 0x48656c6c6f20576f726c64210000000000000000000000000000000000000000 } } ``` -------------------------------- ### Emit Events via Log Opcodes Source: https://context7.com/andreitoma8/learn-yul/llms.txt Shows how to manually emit events using log opcodes. This requires calculating the keccak256 signature and managing memory for non-indexed parameters. ```solidity contract EventExample { event Transfer(address indexed from, address indexed to, uint256 amount); function emitTransfer(address to, uint256 amount) external { assembly { let signature := 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef mstore(0x80, amount) log3(0x80, 0x20, signature, caller(), to) } } } ``` -------------------------------- ### Access Dynamic Array Element via Assembly Source: https://github.com/andreitoma8/learn-yul/blob/master/README.md Demonstrates how to calculate the storage location of a dynamic array element by hashing the array's storage slot and adding the index offset. ```solidity uint256[] arr; function get(uint256 index) public view returns (uint256 value) { uint256 slot; assembly { slot := arr.slot } bytes32 location = keccak256(abi.encode(slot)); assembly{ value := sload(add(location, index)) } } ``` -------------------------------- ### Perform External Calls in Yul Source: https://context7.com/andreitoma8/learn-yul/llms.txt Demonstrates how to interact with other contracts using staticcall and call opcodes. It covers manual memory management for function selectors and arguments, as well as ether transfers. ```solidity contract Caller { function callNoParams(address target) external view returns (uint256) { assembly { mstore(0x00, 0x9a884bde) if iszero(staticcall(gas(), target, 28, 4, 0x00, 0x20)) { revert(0, 0) } return(0x00, 0x20) } } function callWithParams(address target) external view returns (uint256) { assembly { let freeMemPtr := mload(0x40) mstore(freeMemPtr, 0xcad0899b) mstore(add(freeMemPtr, 0x20), 3) mstore(add(freeMemPtr, 0x40), 12) if iszero(staticcall(gas(), target, add(freeMemPtr, 28), 68, 0x00, 0x20)) { revert(0, 0) } return(0x00, 0x20) } } function transferEther(address payable to) external { assembly { if iszero(call(gas(), to, selfbalance(), 0, 0, 0, 0)) { revert(0, 0) } } } } ``` -------------------------------- ### Yul Solidity Inline Assembly Storage Operations Source: https://github.com/andreitoma8/learn-yul/blob/master/README.md Illustrates how to store and retrieve single 256-bit variables from storage using Yul's `sstore` and `sload` opcodes, referencing variables by their `.slot`. This is crucial for direct state manipulation. ```solidity uint256 x; function set(uint256 _x) public { assembly { sstore(x.slot, _x) } } function get() public view returns (uint256 x_) { assembly { x_ := sload(x.slot) } } ``` -------------------------------- ### Updating the Free Memory Pointer in Yul Source: https://github.com/andreitoma8/learn-yul/blob/master/README.md Demonstrates how to manually update the free memory pointer (0x40) after performing memory operations in assembly to prevent collisions with subsequent Solidity code. ```solidity assembly { let freeMemoryPointer := mload(0x40) mstore(freeMemoryPointer, 1) mstore(add(freeMemoryPointer, 0x20), 2) mstore(add(freeMemoryPointer, 0x40), 3) allocate(0x60) function allocate(length) { let pos := mload(0x40) mstore(0x40, add(pos, length)) } } ``` -------------------------------- ### Assign Yul Types and Literals Source: https://github.com/andreitoma8/learn-yul/blob/master/README.md Illustrates how Yul handles bytes32 types and literals, including integers and strings, within a Solidity function. ```solidity function f() public pure returns (bool, uint256, bytes32) { bool x; uint256 y; bytes32 z; assembly { x := 1 y := 0xa z := "Hello World!" } return( x, y, z); } ``` -------------------------------- ### Yul: Call Solidity Contract Function With Parameters Source: https://github.com/andreitoma8/learn-yul/blob/master/README.md Illustrates using Yul's `staticcall` to invoke a Solidity function that accepts parameters. It details how to load the free memory pointer, store the function selector and arguments in memory, and manage the memory pointer. The result of the function call is then returned. ```solidity contract A { function sum(uint256 _a, uint256 _b) external pure returns (uint256) { return _a + _b; } } contract B { function callSum(address _a) external view returns (uint256) { assembly { // load the free memory pointer let freeMemPointer := mload(0x40) // store the function selector of sum(uint256, uint256) in memory mstore(freeMemPointer, 0xcad0899b) // store the first argument of sum(uint256, uint256) in the next memory slot mstore(add(freeMemPointer, 0x20), 3) // store the second argument of sum(uint256, uint256) in the next memory slot mstore(add(freeMemPointer, 0x40), 12) // update the free memory pointer mstore(0x40, add(freeMemPointer, 0x60)) // memory will look like: // 00000000000000000000000000000000000000000000000000000000cad0899b // 0000000000000000000000000000000000000000000000000000000000000003 // 000000000000000000000000000000000000000000000000000000000000000c // call the sum function of contract A // and store the result in memory slot 0x00 if iszero(staticcall(gas(), _a, add(freeMemPointer, 28), 68, 0x00, 0x20)) { revert(0,0) } // return the result from memory slot 0x00 return(0x00, 0x20) } } } ``` -------------------------------- ### Yul Comparison and Bitwise Operations Source: https://context7.com/andreitoma8/learn-yul/llms.txt Demonstrates comparison and bitwise operations in Yul. Comparison functions return 1 for true and 0 for false. Bitwise operations include AND, OR, XOR, NOT, left shift (shl), right shift (shr), and byte extraction. ```solidity function bitwiseOps(uint256 a, uint256 b) public pure returns (uint256, uint256, uint256) { uint256 isEqual; uint256 shifted; uint256 masked; assembly { // Comparison operations isEqual := eq(a, b) // 1 if a == b, 0 otherwise let isLess := lt(a, b) // 1 if a < b let isGreater := gt(a, b) // 1 if a > b let isZero := iszero(a) // 1 if a == 0 // Bitwise operations let andResult := and(a, b) // bitwise AND let orResult := or(a, b) // bitwise OR let xorResult := xor(a, b) // bitwise XOR let notResult := not(a) // bitwise NOT // Shift operations shifted := shl(8, a) // shift a left by 8 bits let rightShift := shr(8, a) // shift a right by 8 bits // Extract specific byte (0 = most significant) masked := byte(0, a) } return (isEqual, shifted, masked); } ``` -------------------------------- ### Handle Solidity fallback calls in Yul Source: https://github.com/andreitoma8/learn-yul/blob/master/README.md Demonstrates how to use a Yul fallback function within a Solidity contract to manually parse function selectors and execute logic. It uses assembly to shift calldata and switch between different function implementations. ```solidity interface IYulContract { function get23() external returns (uint256); function increment(uint256 _value) external returns (uint256); } contract YulContract{ fallback(bytes calldata data) external returns (bytes memory returnData) { assembly{ let callData := calldataload(0) let selector := shr(0xe0, callData) switch case 0x259c137d { returnUint(23) } case 0x7cf5dab0 { returnUint(increment()) } default { revert(0,0) } function returnUint(uint) { mstore(0x00, uint) return(0x00, 0x20) } function increment() -> result { if lt(calldatasize(), 36) { revert(0,0) } let param := calldataload(4) result := add(param, 1) leave } } } } ``` -------------------------------- ### Yul CrowdFunding Contract Deployment Source: https://github.com/andreitoma8/learn-yul/blob/master/CrowdFunding-Contract-Example.md This snippet shows the main Yul object for the CrowdFunding contract. It handles the initial deployment by copying the runtime bytecode and returning its size. It relies on the 'runtime' object for the actual contract logic. ```yul object "CrowdFunding" { code { // return the bytecode of the contract datacopy(0x00, dataoffset("runtime"), datasize("runtime")) return(0x00, datasize("runtime")) } object "runtime" { code { switch selector() case 0x22502268 /* createCampaign(uint256,uint256) target/duration */ { // require enough data is sent for params checkParamLenght(2) // get the campaign ID let id := sload(0x00) // store the ID to memory at 0x00 mstore(0x00, id) // compute the storage slot of the campaing struct let structSlot := keccak256(0x00, 0x20) // store the campaign struct sstore(structSlot, caller()) sstore(add(structSlot, 0x20), calldataload(4)) sstore(add(structSlot, 0x40), add(timestamp() ,calldataload(36))) // increment the ID sstore(0x00, add(id, 1)) } case 0xc1cbbca7 /* contribute(uint256) campaignId */ { // require enough data is sent for param checkParamLenght(1) // require value sent is more than 0 require(lt(0, callvalue())) // get the id from calldata and store it in memory let id := calldataload(4) mstore(0x00, id) // require block timestamp lower than campaign end time let campaignStructSlot := keccak256(0x00, 0x20) let endTimestamp := sload(add(campaignStructSlot, 0x40)) require(lt(timestamp(), endTimestamp)) // sotre caller address to memory mstore(0x20, caller()) // compute the storage slot of the invested value // of the user for this campaign let storageSlot := keccak256(0x00, 0x40) // get the already invested amount let alreadyInvested := sload(storageSlot) // store the new invested value sstore(storageSlot, add(alreadyInvested, callvalue())) // update total raised for campaign ID let totalStorageSlot := add(campaignStructSlot, 0x60) let previousTotal := sload(totalStorageSlot) sstore(totalStorageSlot, add(previousTotal, callvalue())) } case 0x6ef98b21 /* withdrawOwner(uint256) campaignId*/ { // require enough data is sent for params checkParamLenght(1) // store the campaing ID in memory mstore(0x00, calldataload(4)) // get the storage slot of campaign struct let ownerStorageSlot := keccak256(0x00, 0x20) let targetAmountSlot := add(ownerStorageSlot, 0x20) let endTimestampSlot := add(ownerStorageSlot, 0x40) let amountRaisedSlot := add(ownerStorageSlot, 0x60) let amountRaised := sload(amountRaisedSlot) // require caller is the owner require(eq(caller(), sload(ownerStorageSlot))) // require minimum amount is raised require(lt(sload(targetAmountSlot), amountRaised)) // delete the amount raised sstore(amountRaisedSlot, 0) // make end timestamp uint256.max so that // contributors can't withdraw sstore(endTimestampSlot, sub(0,1)) // send value raised to owner if iszero(call(gas(), caller(), amountRaised, 0, 0, 0, 0)) { revert(0,0) } } case 0x152b58ab /* withdrawDonor(uint256) campaignId */{ // require enough data is sent for params checkParamLenght(1) // store the campaing ID in memory mstore(0x00, calldataload(4)) // get the storage slot of campaign struct let ownerStorageSlot := keccak256(0x00, 0x20) let targetAmountSlot := add(ownerStorageSlot, 0x20) let endTimestampSlot := add(ownerStorageSlot, 0x40) let amountRaisedSlot := add(ownerStorageSlot, 0x60) // require that the end timestamp has passed require(lt(sload(endTimestampSlot), timestamp())) // require that the target amount has not been raised require(lt(sload(amountRaisedSlot), sload(targetAmountSlot))) ``` -------------------------------- ### Yul: Delegatecall for Proxy Implementation Source: https://github.com/andreitoma8/learn-yul/blob/master/README.md Provides a Yul code snippet illustrating the use of `delegatecall` within an internal function. This is commonly used in proxy patterns to execute code from an implementation contract in the context of the proxy, preserving the proxy's storage and `msg.sender`. ```solidity function _delegate(address implementation) internal virtual { assembly { ``` -------------------------------- ### Yul: Call Solidity Contract Function Without Parameters Source: https://github.com/andreitoma8/learn-yul/blob/master/README.md Demonstrates how to use Yul's `staticcall` to invoke a function in another Solidity contract that takes no parameters. It shows how to set up the function selector in memory and handle the call's success or failure. The result is returned from memory. ```solidity contract A { // the function selector of 23() is 0x5c60da1b (keccak256("23() của [0:4]) function get21() external returns (uint256) { return 21; } } contract B { function callGet21(address _a) external view returns (uint256) { assembly { mstore(0x00, 0x9a884bde) // // 0000000000000000000000000000000000000000000000000000000009a884bde // last 4 bytes of the memory slot 0x00 are the function selector of getUint() // call the function 23 of contract A // and store the result in memory slot 0x00 if iszero(staticcall(gas(), _a, 28, 4, 0x00, 0x20)) { revert(0, 0) } // return the result from memory slot 0x00 return(0x00, 0x20) } } } ``` -------------------------------- ### Define Standalone Yul Contracts Source: https://context7.com/andreitoma8/learn-yul/llms.txt Illustrates the structure of a pure Yul contract using object notation. It includes a constructor for deployment and a runtime object for execution. ```yul object "HelloWorld" { code { sstore(0, caller()) datacopy(0x00, dataoffset("runtime"), datasize("runtime")) return(0x00, datasize("runtime")) } object "runtime" { code { datacopy(0x00, dataoffset("Message"), datasize("Message")) return(0x00, datasize("Message")) } data "Message" "Hello World" } } ``` -------------------------------- ### Implement utility functions in Yul Source: https://github.com/andreitoma8/learn-yul/blob/master/README.md Provides common utility functions for Yul, specifically a require-style revert mechanism and an address validation check using bitwise operations. ```yul function require(condition) { if iszero(condition) { revert(0, 0) } } function isAddress(value) -> result { if iszero(and(v, not(0xffffffffffffffffffffffffffffffffffffffff))) { result := 1 leave } } ``` -------------------------------- ### Yul Solidity Inline Assembly Bitwise Operations Source: https://github.com/andreitoma8/learn-yul/blob/master/README.md Demonstrates the use of bitwise AND, OR, XOR, NOT, byte extraction, and shift operations within Yul's inline assembly in Solidity. These operations are fundamental for low-level data manipulation. ```solidity function set(uint16 _c) public { assembly{ // Get the storage slot of the variable let wholeSlot := sload(c.slot) // Clear the variable's bits in the slot. Since it is a uint16, it is 2 bytes long. let cleared := and(wholeSlot, 0xffff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff) // Shift the new value to the left by the offset of the variable multiplied by 8(1 byte = 8 bits) let shifted := shl(mul(c.offset, 8), _c) // Combine the cleared slot and the shifted value let newValue := or(shifted, cleared) // Store the new value in the slot sstore(c.slot, newValue) } } function get() public view returns (uint16 c_) { assembly { // Get the storage slot of the variable let wholeSlot := sload(c.slot) // Shift the slot to the right by the offset of the variable let shifted := shr(mul(c.offset, 8), wholeSlot) // Mask the slot to get the value of the variable c_ := and(shifted, 0xffff) } } ``` -------------------------------- ### Define a standalone Yul contract Source: https://github.com/andreitoma8/learn-yul/blob/master/README.md Shows the structure of a fully Yul-based contract using objects. It defines a constructor code section and a runtime object that returns a static message. ```yul object "FullyYul" { code { sstore(0, caller()) datacopy(0x00, dataoffset("runtime"), datasize("runtime")) return(0x00, datasize("runtime")) } object "runtime" { code { datacopy(0x00, dataoffset("Message"), datasize("Message")) return (0x00, datasize("Message")) } data "Message" "Hello World" } } ``` -------------------------------- ### Yul Basic Arithmetic Operations Source: https://context7.com/andreitoma8/learn-yul/llms.txt Illustrates basic arithmetic operations in Yul, including addition and subtraction. Yul operations do not have overflow protection and are executed from innermost to outermost expressions. Other operations like division and modulo are also available. ```solidity function arithmetic() public pure returns (uint256, uint256, uint256) { uint256 a; uint256 b; uint256 c; assembly { a := add(10, 5) // 15 - addition b := sub(10, 3) // 7 - subtraction c := add(1, mul(2, 3)) // 7 - innermost mul(2,3)=6 executed first, then add(1,6)=7 // Other operations: // div(x, y) - division // mod(x, y) - modulo } return (a, b, c); } ``` -------------------------------- ### Encode Dynamic Calldata in Assembly Source: https://github.com/andreitoma8/learn-yul/blob/master/README.md Shows how to manually construct calldata for a function with dynamic arguments (arrays) using memory pointers. This is useful for low-level calls when the ABI encoder is bypassed. ```Solidity assembly { let freeMemPointer := mload(0x40) mstore(freeMemPointer, 0xbfda4ee2) mstore(add(freeMemPointer, 0x20), 1) mstore(add(freeMemPointer, 0x40), 0x40) mstore(add(freeMemPointer, 0x60), 2) mstore(add(freeMemPointer, 0x80), 3) mstore(add(freeMemPointer, 0xa0), 1) mstore(0x40, add(freeMemPointer, 0xc0)) if iszero(staticcall(gas(), _a, add(freeMemPointer, 28), 164, 0x00, 0x20)) { revert(0,0) } return(0x00, 0x20) } ``` -------------------------------- ### Yul CrowdFunding Contract Implementation Source: https://context7.com/andreitoma8/learn-yul/llms.txt This Yul code defines a complete crowdfunding contract. It includes functions for creating a campaign and accepting contributions, demonstrating significant gas savings by working directly with EVM opcodes. It utilizes Yul's object notation for constructor and runtime code. ```yul object "CrowdFunding" { code { datacopy(0x00, dataoffset("runtime"), datasize("runtime")) return(0x00, datasize("runtime")) } object "runtime" { code { switch selector() // createCampaign(uint256 target, uint256 duration) case 0x22502268 { checkParamLength(2) let id := sload(0x00) mstore(0x00, id) let structSlot := keccak256(0x00, 0x20) sstore(structSlot, caller()) // owner sstore(add(structSlot, 0x20), calldataload(4)) // target sstore(add(structSlot, 0x40), add(timestamp(), calldataload(36))) // endTime sstore(0x00, add(id, 1)) } // contribute(uint256 campaignId) case 0xc1cbbca7 { checkParamLength(1) require(lt(0, callvalue())) let id := calldataload(4) mstore(0x00, id) let structSlot := keccak256(0x00, 0x20) require(lt(timestamp(), sload(add(structSlot, 0x40)))) mstore(0x20, caller()) let donorSlot := keccak256(0x00, 0x40) sstore(donorSlot, add(sload(donorSlot), callvalue())) let totalSlot := add(structSlot, 0x60) sstore(totalSlot, add(sload(totalSlot), callvalue())) } default { revert(0, 0) } function selector() -> s { s := div(calldataload(0), 0x100000000000000000000000000000000000000000000000000000000) } function require(condition) { if iszero(condition) { revert(0, 0) } } function checkParamLength(len) { require(eq(calldatasize(), add(4, mul(32, len)))) } } } } ``` -------------------------------- ### Implement Safe Math in Yul Source: https://context7.com/andreitoma8/learn-yul/llms.txt Provides utility functions for overflow-safe arithmetic and input validation. Since Yul lacks built-in overflow protection, these functions manually check results before proceeding. ```yul function safeAdd(a, b) -> result { result := add(a, b) if or(lt(result, a), lt(result, b)) { revert(0, 0) } } function safeSub(a, b) -> result { if lt(a, b) { revert(0, 0) } result := sub(a, b) } function safeMul(a, b) -> result { switch a case 0 { result := 0 } default { result := mul(a, b) if iszero(eq(div(result, a), b)) { revert(0, 0) } } } function require(condition) { if iszero(condition) { revert(0, 0) } } function isValidAddress(value) -> result { result := iszero(and(value, not(0xffffffffffffffffffffffffffffffffffffffff))) } ``` -------------------------------- ### Yul: Low-level Contract Logic for Fund Transfer and Checks Source: https://github.com/andreitoma8/learn-yul/blob/master/CrowdFunding-Contract-Example.md This Yul code snippet demonstrates low-level contract logic for handling fund transfers and performing essential checks. It includes functions for storing data, calculating storage slots, requiring conditions to be met, checking calldata length, and transferring Ether. This code is typically used for highly optimized smart contract development. ```Yul mstore(0x20, caller()) // compute the storage slot of the invested value // of the user for this campaign let storageSlot := keccak256(0x00, 0x40) let amountToSend := sload(storageSlot) // require that the amount to send is bigger than 0 require(lt(0, amountToSend)) sstore(storageSlot, 0) // send back funds to doner transfer(amountToSend) } default { // if the function signature sent does not match any // of the contract functions, revert revert(0, 0) } // Return the function selector: the first 4 bytes of the call data function selector() -> s { s := div(calldataload(0), 0x100000000000000000000000000000000000000000000000000000000) } // Implementation of the require statement from Solidity function require(condition) { if iszero(condition) { revert(0, 0) } } // Check if the calldata has the correct number of params function checkParamLenght(len) { require(eq(calldatasize(), add(4, mul(32, len)))) } // Transfer ether to the caller address function transfer(amount) { if iszero(call(gas(), caller(), amount, 0, 0, 0, 0)) { revert(0,0) } } } } } ``` ``` -------------------------------- ### Access Mapping of Arrays via Assembly Source: https://github.com/andreitoma8/learn-yul/blob/master/README.md Retrieves an element from an array stored within a mapping by performing nested hashing of the key and slot, then adding the array index. ```solidity mapping(address => uint256[]) map; function get(address key, uint256 index) public view returns (uint256 value) { bytes32 slot; assembly { slot := map.slot } bytes32 location = keccak256(abi.encode(keccak256(abi.encode(key, uint256(slot))))); assembly{ value := sload(add(location, index)) } } ``` -------------------------------- ### Yul Control Flow Statements Source: https://context7.com/andreitoma8/learn-yul/llms.txt Shows Yul's control flow statements: for loops, if statements, and switch statements. Yul treats any non-zero value as truthy and uses functions like `gt` (greater than) and `iszero` for conditional logic. ```solidity function controlFlow(uint256 n) public pure returns (uint256) { uint256 result; assembly { // For loop for { let i := 0 } lt(i, n) { i := add(i, 1) } { result := add(result, 1) } // If statement (any non-zero value is true) if gt(result, 5) { result := mul(result, 2) } // Negation using iszero if iszero(0) { // This executes because iszero(0) returns 1 (true) } // Switch statement switch result case 0 { result := 100 } case 1 { result := 200 } default { result := add(result, 50) } } return result; } ``` -------------------------------- ### Yul Solidity Inline Assembly Packed Array Storage Access Source: https://github.com/andreitoma8/learn-yul/blob/master/README.md Demonstrates retrieving a value from a packed array in Yul. It involves loading the entire `bytes32` slot and then using bitwise shifts (`shr`) to isolate the desired smaller-than-32-byte variable. ```solidity uint128[4] arr; function getIndex1() public view returns (uint128 value) { bytes32 packed; assembly { // Get the first bytes32 of the array packed := sload(arr.slot) // Shift the bytes32 to the right by 16 bytes(128 bits) to get the value of the first variable value := shr(mul(16, 8), packed) } } ``` -------------------------------- ### Yul Solidity Inline Assembly Fixed Array Storage Access Source: https://github.com/andreitoma8/learn-yul/blob/master/README.md Shows how to access elements of a fixed-size array stored in Yul by directly calculating the storage slot using the array's base slot plus the index. This method is used for retrieving `bytes32` values. ```solidity uint256[5] arr; function get(uint256 index) public view returns (uint256 value) { assembly { value := sload(add(arr.slot, index)) } } ```