Solidity Yul Assembly – A Beginners Guide

Solidity is the most popular programming language in the domain of blockchain. It is a powerful alternative for scripting smart contracts and offers a user-friendly development experience. If you want to learn Solidity Yul Assembly, then you have to dive deeper into the functionalities of Solidity. You can tap into the true potential of the Ethereum Virtual Machine or EVM by exploring the details of Solidity. Assembly is one of the important factors in the working of Solidity.

It is the low-level language that helps developers explore the inner working mechanisms of Ethereum Virtual Machine or EVM in detail. Assembly helps in optimization of smart contracts to achieve better performance and efficiency. It serves as an additional functionality for optimizing every line of code and drawing the best from smart contracts. Let us learn more about Solidity Yul Assembly in the following discussion.

Learn the process of creating and deploying smart contracts on the Ethereum blockchain with the Solidity Fundamentals Course.

Working of EVM and Opcodes

Before you write Assembly code, you must learn about the workings of EVM. You can understand a Solidity Yul Assembly guide with more clarity by exploring the working mechanisms of EVM and opcodes. The Ethereum Virtual Machine, or EVM, is one of the core components of the Ethereum blockchain. It serves as a universal decentralized computer for the execution of smart contracts alongside ensuring reliability and consistency throughout the network. What is the procedure for working with EVM?

Upon compilation of a contract, you would receive a bytecode. The bytecode is a long collection of bytes and serves as a representation of a collection of tiny instructions. Each instruction includes 1 byte, and they are termed as opcodes. What is the relationship between Solidity inline assembly and opcodes? Opcodes help in performing different operations, such as memory manipulation, storage access, arithmetic calculations, and control flow management.

Learn the basic and advanced concepts of Ethereum? Enroll now in theEthereum Development Fundamentals Course

Definition of Assembly in Solidity

The next important aspect you need to learn for exploring a Solidity inline assembly example is the definition of assembly. Assembly or inline assembly is the low-level language that enables low-level access to the Ethereum Virtual Machine. You can think of it as a special pass for the underlying working mechanisms of the Ethereum Virtual Machine. Assembly can help you write code for bypassing specific safety features and important checks in Solidity. As a result, developers would have more control over their smart contracts.

You can find a better answer to What is Solidity Yul Assembly? by identifying the language used for writing assembly in Solidity. The importance of Yul in writing assembly in Solidity validates the use of Yul as a must-have term for explaining inline assembly. Yul is an intermediate language and offers the flexibility for compiling code into bytecode for the Ethereum Virtual Machine. At any point in writing code in Solidity, you can use the assembly { } keyword to begin writing inline assembly.

You can find different control levels when you insert the assembly block in Solidity code. Solidity comes first and offers a high-level approach to writing smart contracts. On the other hand, you can want more control by using Yul or assembly language. Yul helps in manipulation of the EVM at a more granular level that can offer the flexibility of fine-tuning the code and improving efficiency. If you want a bit more adventure, then you can write bytecode directly for the EVM. It would not require a compiler and is similar to gaining complete control over the EVM.

Build your identity as a certified blockchain expert with 101 BlockchainsBlockchain Certificationsdesigned to provide enhanced career prospects.

How Can You Write Inline Assembly in Solidity?

The next crucial highlight in learning about inline assembly in Solidity with Yul programming language is the example code. You can use a Solidity inline assembly example for a simple contract, termed a Box. The contract would help in storing a value, modifying the value, and retrieving it. Here is the example code for the Box contract.

pragma solidity ^0.8.14;

contract Box {
  uint256 private _value;

  event NewValue(uint256 newValue);

  function store(uint256 newValue) public {
    _value = newValue;
    emit NewValue(newValue);
  }

  function retrieve() public view returns (uint256) {
    return _value;
  }
}

Now, you can check the Solidity assembly call function by converting the example Solidity code to inline assembly. You can start working on the retrieve function from the first step. When you take the example of the original Solidity code, the retrieve function works on reading the value stored in the _value parameter from the contract storage and returns the result.

In the case of assembly, you can achieve a similar result by using the sload opcode for reading the value. The sload opcode would receive one input, the key of the storage slot, and the _value variable would be stored in slot #0. In assembly language, you can write the following example.

assembly {
  let v := sload(0) // Read from slot #0
}

After you obtain the value, you would have to work on returning it. In the case of Solidity inline assembly, you can rely on the return opcode to accomplish the function. The return opcode would take two distinct inputs, such as offset and size. The offset input denotes the location where the value starts in the memory, and size refers to the number of bytes that it should return.

On the other hand, the sload returns the value v, which is stored in the call stack rather than the memory. Therefore, you have to move it to memory before retrieving the value. The opcode mstore can help in storing the value in memory by taking two inputs, such as offset and value. The offset parameter denotes the location in the memory array where you should store the value, and value refers to the number of bytes or v.

The final assembly code for the Solidity inline assembly example would look like the following.

assembly {
  let v := sload(0) // Read from slot #0
  mstore(0x80, v) // Store v at position 0x80 in memory
  return(0x80, 32) // Return 32 bytes (uint256)
}

You must note a special highlight in the assembly code, which chooses the 0x80 position in the memory specifically for storing the value. Why? The review of a Solidity Yul Assembly guide must also help you learn about the answer. Solidity blocks the first four 32-byte slots for specific applications. Therefore, free memory would start from 0x80. The simple example outlined in this discussion enables the use of 0x80 for storing the new variable. On the other hand, complex operations would require tracking of a pointer for free memory and ensuring effective management.

function retrieve() public view returns (uint256) {
  assembly {
    let v := sload(0)
    mstore(0x80, v)
    return(0x80, 32)
  }
}

How to Use the Store Function in the Given Example?

The comprehensive explanation of answers to What is Solidity Yul Assembly? would also draw attention to the store function. It helps in storing a variable with the sstore opcode that would take two inputs. One of the inputs is key, which is a 32-byte key in storage, and value is the value that you have to store. In assembly language, you can write the function as the following example:

assembly {
 sstore(0, newValue) // store value at slot 0 of storage
}

After storing the value, you can begin the process of transferring an event by utilizing the log1 opcode. The log1 opcode requires three different inputs such as offset, topic, and size. The offset input denotes the byte offset in the memory where you have to store the event data. Size input denotes the size of the data that you have to copy in bytes.

The topic input refers to the 32-byte value, which works as an identifier or label for the event. Candidates who want to learn Solidity Yul Assembly practical knowledge should know that log1 opcode must set the three inputs to different values. You should set the offset to 0x80 as you have stored the value by using mstore opcode.

The size input for the opcode can be specified as 0x20, which serves as a representative of 32 bytes. Finally, you must set the topic parameter as a label for an event such as the name NewValue. The argument passed for topic is just the hash of the event signature. By using these updates, the store function would look like the following.

function store(uint256 newValue) public {
 assembly {
 // store value at slot 0 of storage
 sstore(0, newValue)

 // emit event
 mstore(0x80, newValue)
 log1(0x80, 0x20, 0xac3e966f295f2d5312f973dc6d42f30a6dc1c1f76ab8ee91cc8ca5dad1fa60fd)
 }
}

Finally, the Box contract would look like the following.

pragma solidity ^0.8.14;

contract Box {
  uint256 public value;

  function retrieve() public view returns(uint256) {
    assembly {
      let v := sload(0) 
      mstore(0x80, v)
      return(0x80, 32) 
    }
  }

  function store(uint256 newValue) public {
    assembly {
      sstore(0, newValue)
      mstore(0x80, newValue)
      log1(0x80, 0x20, 0xac3e966f295f2d5312f973dc6d42f30a6dc1c1f76ab8ee91cc8ca5dad1fa60fd)
    }
  }
}

Certified web3 Professional Certification

How Can You Use Solidity Yul Assembly to Send Ether to an Address?

The functions of Solidity assembly call and its efficiency also allow you to design another contract for sending Ether to an address. Here is an example of a contract that can help you achieve the same functionalities.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.14;

contract MyContract {

  address public owner = payable(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4);

  function sendETH(uint256 _amount) public payable {
    require(msg.value >= _amount,"Not Enough ETH Sent");
    bool success;
    assembly {
      let o := sload(0)
      success := call(gas(), o, _amount, 0, 0, 0, 0)
    }
    require(success, "Failed to send ETH");
  }
}

Here is a breakdown of the assembly code in the individual steps.

First of all, slot 0 stores the owner’s address, and the owner’s address is assigned to the local variable o. In addition, the sload opcode helps in reading values from storage. In the next line of the Solidity inline assembly example, you can notice the execution of the call opcode. It helps in sending Ether to a specific address, and the call opcode in assembly language would take different arguments.

The gas() function works on returning the remaining gas for the existing execution context. It can be passed in the form of the first argument for calling that would indicate the provision of a maximum amount of gas for the function calls.

The call opcode also takes the address argument, which represents the address of the contract or user that it has to call. It has the value that is located in slot 0 storage.

The value argument represents the amount of Ether that you have to send through the function call. In the case of this example, the value argument must be passed as the second argument for calling.

You can understand Solidity inline assembly functionalities by pointing at the next four arguments, i.e. (0, 0, 0, 0). The arguments help in passing additional data to the function that you want to call. In the example code, you can notice that they have been set to zero to ensure that they dont pass any additional data.

The call opcode outcomes can be assigned to the success local variable. It would be true when the function call is successful and false in the contrary situation.

Excited to build your skill in Ethereum development by leveraging the ethers.js library? Enroll now in theEthers.Js Blockchain Developer Course

What are the Limitations of Solidity Assembly Language?

The details in a Solidity Yul Assembly guide can help you understand that low-level language would present certain limitations. First of all, it is difficult to understand, and beginners might not understand the logic and flow of the code instantly. It could be an intimidating concept for people who are not familiar with low-level programming. However, it is important to note that such limitations do not affect the value benefits of assembly in Solidity. Assembly code could help in improving competitive advantage and gas efficiency in Solidity.

Start your journey to become a smart contract developer or architect withSmart Contract Skill Path

Conclusion

The applications of Yul programming language to create inline assembly code in Solidity help in optimizing smart contracts. The primary goal of Solidity inline assembly revolves around tailoring smart contracts to achieve the desired goals of a smart contract precisely. Assembly code could look unappealing and difficult as it is scripted in low-level language.

On the other hand, it can offer a comprehensive range of benefits that can help in achieving better cost savings and gas efficiency. Developers should account for all the trade-offs and evaluate whether the complexity of assembly code is the right price to pay for potential advantages in particular use cases.

Unlock your career with 101 Blockchains' Learning Programs

This article was originally reported on 101 Blockchains.