Reader small image

You're reading from  Solidity Programming Essentials. - Second Edition

Product typeBook
Published inJun 2022
PublisherPackt
ISBN-139781803231181
Edition2nd Edition
Concepts
Right arrow
Author (1)
Ritesh Modi
Ritesh Modi
author image
Ritesh Modi

Ritesh Modi is a technologist with more than 18 years of experience. He holds a master's degree in science in AI/ML from LJMU. He has been recognized as a Microsoft Regional Director for his contributions to building tech communities, products, and services. He has published more than 10 tech books in the past and is a cloud architect, speaker, and leader who is popular for his contributions to data centers, Azure, Kubernetes, blockchain, cognitive services, DevOps, AI, and automation.
Read more about Ritesh Modi

Right arrow

Chapter 11: Assembly Programming

Solidity is a high-level language. A Solidity compiler does not generate assembly code; instead, it generates Intermediate Code (IL). This IL code is understood by the Ethereum Virtual Machine (EVM).

The EVM, on the other hand, does not understand Solidity constructs. It loads the IL code and executes it by interpreting it. This IL code is known as bytecode within the Ethereum ecosystem.

All activities, including contract deployment, contract function invocation, and the simple transfer of Ether between accounts, lead to bytecode execution by the EVM. Bytecode comprises instructions in hexadecimal format. The instructions or bytecode built by Solidity compiler (solc) comprise a series of opcodes along with their input values in hexadecimal format.

Opcodes are human-readable; however, they have equivalent hexadecimal representation, which is what we see as bytecode. It's easier to think of opcodes as functions that accept arguments and...

Technical requirements

The following tools are required for working along with this chapter:

  • A Chrome browser
  • An online Remix editor

The code from this chapter can be found on GitHub at https://github.com/PacktPublishing/Solidity-Programming-Essentials-Second-Edition/tree/main/Chapter11.

An introduction to Solidity and its advantages

Assembly programming in Solidity is low-level programming in which we can use opcodes and work with them directly. There are many advantages to using assembly language while writing contracts. The major advantages include the following:

  • Added capability: There are a few things that can only be done using assembly programming in Solidity. Some of these capabilities are not available in Solidity grammar itself – for example, determining whether an address is a contract address can be ascertained in assembly but not in Solidity. It should be noted that Solidity developers are trying to minimize this gap in the newer versions of the language.
  • Optimization of gas usage: We can optimize code by writing assembly code because it has fewer instructions compared to Solidity compiler-generated code.
  • Having full control: Writing assembly language directly gives us more control over generated bytecode compared to the compiler...

Getting started with Assembly programming

Assembly code can be mixed with Solidity code using the assembly keyword, followed by brackets defining a block. There can be as many assembly blocks as needed within a Solidity function. The SimpleAssembly contract shown in the following code block shows the usage of Solidity code intertwined with multiple assembly blocks:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
 contract SimpleAssembly {
    function AssemblyUsage() public pure returns (uint256) {
        
        uint256 i = 10;
        assembly {
            i := 100
        }
        assembly {
            i := 200...

Scopes and blocks

Assembly blocks can, in turn, have nested blocks. A block is a scope, and any variables declared within a scope get deallocated as soon as the execution leaves the block. Each block defines a local scope.

Variables declared within nested blocks are not visible outside of the block. They get deallocated after the block execution completes. The innerValue variable declared within the inner block cannot be used outside the block. It is visible only inside the block in which it is declared. The same goes for the outerValue variable, which is not accessible outside the assembly block but is available for reading inside its nested blocks.

Moreover, variables declared in the parent scope cannot be redefined in the inner scope or block. The outerValue is already declared in the parent scope, and trying to redefine it using the let keyword within an inner block raises a variable name is already taken compile-time error.

Also, variables declared in one scope cannot...

Returning values

Assembly blocks can return values to their parent scope. Since variables get deallocated after the block execution, returning values from an assembly block can become quite important. There are a couple of ways to return values from the assembly block.

One way to return values from a function that, in turn, gets a value from assembly code is to use a variable name in the function signature. The same variable should be assigned a value from the assembly code. The next function shown declares a return type using retval as its name. In assembly code, the retval variable is assigned a constant value, and this gets automatically returned as part of the function's return value. Note that the return keyword or opcode is not used in this case:

function usingReturnVariable() public pure returns (uint256 
  retval) {
            assembly {
      ...

Working with memory slots

Solidity provides opcodes for working with memory variables in assembly code. We already know that there are three types of variables – storage, memory, and calldata. Assembly language can work with all three.

Assembly provides the mload and mstore opcodes for loading or storing values in memory variables. The mload opcode helps to load or read the memory location and the returning data stored therein. The mstore opcode helps to store data in memory. The usage of both mload and mstore is shown using a smart contract in the following code block:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract MemoryAssembly {
    function AssemblyUsage() public pure returns (uint256) 
{
        assembly {
            let addresult := add(100,200)
          &...

Working with storage slots

Similar to memory variables, Solidity also provides opcodes for working with state variables. The state variables' related opcodes are sload and sstore. Again, similar to memory functions, sload reads the value stored in the storage slot and returns the value. It accepts the storage slot location as its only argument. sstore updates the value at a given storage slot. It accepts the storage slot as its first argument and the value to be stored as its second argument. The storage slot is created if it already does not exist. The usage of both sload and sstore is shown using a smart contract in the following code block:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract StorageAssembly {
    uint256 StateVariable;
    function AssemblyUsage() public returns (uint256 
      newstatevariable, uint256 newderivedvariable) {
     ...

Calling contract functions

Assembly code also allows interaction with other contracts. One of the main use cases is to invoke functions on other contracts and get return value from them. Solidity provides the call opcode that can call functions in a contract. The signature of the call opcode is shown here:

call(g, a, v, in, insize, out, outsize)

Here, g stands for the amount of gas being sent with the call, a stands for the address of the target contract, v stands for the amount of Ether being sent in wei denomination, in stands for the starting memory location containing the data to be sent to the EVM (which comprises the method signature and its parameter values), insize is the size of data being sent in the hexadecimal format, out stands for the starting memory location that should store the return data from the call, and outsize is the size of return data in hexadecimal format.

We will use the call function to invoke a function in another contract, TargetContract. This...

Determining contract addresses

Now that we understand the process of calling a contract function using Solidity assembly programming, another important coding requirement is to find out whether a given address belongs to an externally owned account or a contract account. This is especially useful when writing ERC20 and ERC721 token contracts.

To demonstrate how to determine whether an address is a contract address or an externally owned account, the same TargetContract contract shown in the previous section will be used. The TargetContract contract should be available on the Ethereum network and can be accessed using its address. The address of the TargetContract address can be supplied as an argument to the CheckIfContract function shown next. This function uses the assembly extcodesize opcode to determine the type of address supplied to it. It is a contract account if the length of the value returned by extcodesize opcode is greater than zero; otherwise, it belongs to a user...

Summary

This chapter was relatively more complex compared to previous chapters. It dealt primarily with assembly programming within Solidity. Assembly is a comprehensive language, and all of its aspects cannot be covered in a single chapter.

This chapter covered some important assembly programming-related concepts, such as declaring variables, blocks, scope, and returning values from assembly blocks and from a function itself. Some advanced concepts, such as working with memory and storage data, were also covered in the chapter.

Some concepts, such as looping and conditional statements, were not covered within this chapter. If you are interested in learning about them, read the official Solidity documentation for more information. Next, it's time to learn how to write maintainable and modular smart contracts in Solidity using upgradable contracts, which we will discuss at length in the next chapter.

Questions

  1. What are the opcodes associated with reading and updating storage slots?
  2. What is the opcode used to ascertain whether an address is a contract address or belongs to an externally owned account?
  3. At which location is the memory-free pointer available?

Further reading

lock icon
The rest of the chapter is locked
You have been reading a chapter from
Solidity Programming Essentials. - Second Edition
Published in: Jun 2022Publisher: PacktISBN-13: 9781803231181
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Author (1)

author image
Ritesh Modi

Ritesh Modi is a technologist with more than 18 years of experience. He holds a master's degree in science in AI/ML from LJMU. He has been recognized as a Microsoft Regional Director for his contributions to building tech communities, products, and services. He has published more than 10 tech books in the past and is a cloud architect, speaker, and leader who is popular for his contributions to data centers, Azure, Kubernetes, blockchain, cognitive services, DevOps, AI, and automation.
Read more about Ritesh Modi