What is Yul?
Written by Mark Jonathas
Reviewed by Brady Werkheiser
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.
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.
Variable Assignments, Operations, & Evaluations in Yul
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!
Let’s take a quick look at an example before moving on.
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;
}
For Loops & If Statements in Yul
To learn about both For loops and If statements, let’s write a function that counts how many numbers are even in a series.
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.
How Storage Works in Yul
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:
Arrays
Dynamic arrays
Mappings
Nested Mappings
How to Read & Write Packed Variables in Yul
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!
How does memory work in Yul?
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:
Returning values for external calls
Setting function values for external calls
Getting values from external calls
Reverting with an error string
Logging messages
To learn more about how Yul memory works, read our other article that dives deeper into the topic!
How do contract calls work in Yul?
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:
call()
- call contract at address a returning 0 on error and 1 on successstaticcall()
- identical to call(g, a, 0, in, insize, out, outsize) but do not allow state modificationsdelegatecall()
- identical to callcode but also keep caller and callvalue
More information about contract call definitions can be found in the Yul language documentation.
Related overviews
What it is, How it Works, and How to Get Started
Explore the Best Free and Paid Courses for Learning Solidity Development
Your Guide to Getting Started With Solidity Arrays—Functions, Declaring, and Troubleshooting