0%
Overview page background
HomeOverviewsLearn Solidity
What is Yul?

What is Yul?

Mark Jonathas headshot

Written by Mark Jonathas

Brady Werkheiser headshot

Reviewed by Brady Werkheiser

Published on July 28, 20233 min read

Yul is an intermediate programming language that can be used to write a form of assembly language inside smart contracts. Although we often see Yul used inside smart contracts, you can write a smart contract entirely in Yul. 

Understanding Yul can level up your smart contracts and allow you to understand what is going on under the hood in solidity, which in turn can help you save user’s gas costs. 

We can identify Yul in a smart contract with the following syntax.

Copied
assembly {     // do stuff   }

In the remainder of this article, we will discuss the basics of using Yul through examples, and we encourage you to follow along in Remix.

The first topic we need to cover is simple operations. Yul has +, -, *, /, %, **, <, >, and =.

Notice that >= and <= are not included in the list of available Yul operators because Yul does not have these operators. Additionally, instead of evaluations equaling true or false, they equal 1 or 0, respectively. With that said, let's get started learning some Yul!

Variable Assignments, Operations, & Evaluations in Yul
Variable Assignments, Operations, & Evaluations in Yul

Let’s take a quick look at an example before moving on.

Copied
function addOneAnTwo() external pure returns(uint256) {     // We can access variables from solidity inside our Yul code     uint256 ans;     assembly {         // assigns variables in Yul         let one := 1         let two := 2         // adds the two variables together         ans := add(one, two)     }     return ans; }

To learn about both For loops and If statements, let’s write a function that counts how many numbers are even in a series.

Copied
function howManyEvens(uint256 startNum, uint256 endNum) external pure returns(uint256) {       // the value we will return     uint256 ans;       assembly {           // syntax for for loop         for { let i := startNum } lt( i, add(endNum, 1) { i := add(i,1) }         {             // if i == 0 skip this iteration             if iszero(i) {                 continue             }               // checks if i % 2 == 0             // we could of used iszero, but I wanted to show you eq()             if  eq( mod( i, 2 ), 0 ) {                 ans := add(ans, 1)             }           }       }         return ans;   }

The syntax for if statements are similar to Solidity, but you don’t need to put the condition in parentheses.

For the for loop, notice we use brackets when declaring i and incrementing i, but not when evaluating the condition. Additionally, we used a continue to skip an iteration of the loop. We can also use break statements in Yul as well.

There are 2²⁵⁶ slots for a smart contract. When declaring variables, we start with slot 0 and increment from there. Each slot is 256 bits long (32 bytes); that’s where uint256 and bytes32 get their names. All variables are converted to hexadecimal. If a variable, such as a uint128, is used, we do not take an entire slot to store that variable. Instead, it is padded with 0’s on the left side.

To learn more about how Yul storage works, read our other article that covers:

  1. Arrays

  2. Dynamic arrays

  3. Mappings

  4. Nested Mappings

Packing variables means ordering variables such that the sum of their storage is under 32 bytes so they can fit into a slot together! Packing variables can save gas, and is an essential technique for smart contract optimizers.

For example, 0x000000000000000000000000000002 and 0x000000000000000000000000000001 fit perfectly together in the same slot because they both take up 16 bytes (half of a slot).

To learn more about how to read and write packed storage variables in Yul, read our deep dive!

Memory behaves differently than storage as it is not persistent (i.e. once the function is done executing all of the variables are cleared). Some uses of memory in Yul are: 

  1. Returning values for external calls

  2. Setting function values for external calls

  3. Getting values from external calls

  4. Reverting with an error string

  5. Logging messages

To learn more about how Yul memory works, read our other article that dives deeper into the topic!

Once you understand syntax, storage, and how memory works in Yul, it’s time to call contracts. Our final article in the Yul series explains how contracts are called in Yul, specifically via:

  1. call() - call contract at address a returning 0 on error and 1 on success

  2. staticcall() - identical to call(g, a, 0, in, insize, out, outsize) but do not allow state modifications

  3. delegatecall() - identical to callcode but also keep caller and callvalue‍

More information about contract call definitions can be found in the Yul language documentation.

Overview cards background graphic
Section background image

Build blockchain magic

Alchemy combines the most powerful web3 developer products and tools with resources, community and legendary support.

Get your API key