# CS4998: Blockchain Development

Fall 2023

The official textbook for CS4998: Blockchain Development offered at Cornell University. This book focuses on teaching students both the theorhetical and applied skills necessary to become a proficient blockchain developer.

CS4998: Blockchain Development teaches students how to program using the Solidity programming language. Throughout this students, students are also exposed to languages such as Python for on-chain data interaction and libraries such as Foundry for local contract development.

**Course Number**: 19848

**Authors**: Rodrigo Villar (Head of Research @ Cornell Blockchain), Abhishek Bhattacharya

**Copyright**: Released under the [Creative Commons Attribution-NonCommericial-NoDerivatives 4.0 International License](http://creativecommons.org/licenses/by-nc-nd/4.0/)


# Prerequisites

What You Need To Already Know

Students who wish to take CS4998 are expected to have satisfied the following prerequisites:

* CS1110: Introduction to Computing
* CS2110: Object-Oriented Programming and Data Structures

In addition to the prerequisites listed above, *it is highly suggested that students have previously taken* CS1998: Introduction to Blockchain. However, if a student is confident that they poccess the necessary blockchain knowledge, they can take CS4998.


# Blockchain Theory

Understanding What You're Writing On

Prior to developing applications on *any* blockchain, it is important that one understands the platform that they're building on. This is in contrast to writting programs in languages like Python, where we can just "assume" that they'll run as expected. We begin this section by proposing the following question:

> What is a blockchain?

From the perspective of a computer scientist, one might claim that a blockchain is an append-only linked-list of blocks where each block contains a list of transactions. Whether you are first learning about blockchain or talking about blockchain amongst your friends, this definition should be enough. However, consider the following questions:

* How do we only allow valid transactions to be included in appended blocks?
* Where do smart contracts live?

Questions like the above expose the flaws of viewing blockchains as *solely append-only linked-lists*. In this section, we will transition from viewing blockchains as simple data structures towards viewing blockchains as distributed computers which are able to do so much more.


# Bitcoin and the UTXO Model

The Original Blockchain

In our journey towards interpreting blockchains as distributed computers, it makes sense to examine to examine Bitcoin, the first blockchain. Although Bitcoin's capabilities are limited, its simplicity allows us to understand the VM that powers Bitcoin in its entirety.

## History of Bitcoin

2008 is a landmark year if we talk about significant impact across a wide variety of aspects of the world. Be it the Global Financial Crisis, the Presidential Election which resulted in the first African American, Barack Obama, elected as the President of the United States, or the Large Hadron Collider which was successfully launched to study the fundamental properties of matter.&#x20;

What people came to recognize in subsequent years was another landmark event from 2008 which set the course for not only an alternative currency, but also revolutionized the the world by giving birth to Blockchain Technology. This is about the 8-pages that shook the world of Finance, to start with. Here’s a link to the Bitcoin Whitepaper hosted by the USSC: [https://www.ussc.gov/sit... ](https://www.ussc.gov/sites/default/files/pdf/training/annual-national-training-seminar/2018/Emerging_Tech_Bitcoin_Crypto.pdf)

This whitepaper led to the emergence of Bitcoin as a decentralized digital currency and a distributed ledger technology – Blockchain. Bitcoin is a decentralized digital currency that operates on a peer-to-peer network, eliminating the need for intermediaries such as banks or financial institutions. This definition itself implies an overhaul of the finance system as we’ve known since decades. We have always known to trust a central, regulatory agency such as a bank or the Central Bank of a country.&#x20;

It is not like digital currencies were not in existence before the 2008 Bitcoin whitepaper launch. However, they suffered from a typical central issue – the problem of double- spending, which implies that a single digital token in a digital cash protocol can be spent more than once.&#x20;

Bitcoin was able to solve this problem by defining the fundamental principles and mechanisms of a networking and architectural brilliance which later came to be known as Blockchain Technology – a decentralized ledger that records all transactions in the format of a chain of blocks, where each new block is linked to the previous block by means of a cryptographic hash, forming a chronological chain.

&#x20;Few of the most important characteristics of blockchain got defined by this very design of the technology. It ensures that every piece of data (transactions) is immutable, transparent, and verifiable by anyone who’s a participant of the blockchain network. This tamper-proof nature of the technology is what made blockchain significantly different from vanilla databases.

So, how was the problem of double-spending solved by blockchain, specifically Bitcoin? The concept at the core of this is **Mining**. As a matter of fact, Bitcoin as a digital currency was created largely as an incentive to network participants who would work towards validating the transactions and keeping the network secure. This process of validation is called mining. For this to happen, the miners (nodes involved in the process of mining) harness substantial computational power to tackle complex mathematical puzzles, eventually facilitating the process of validating transactions and subsequently adding new blocks to the blockchain. As a reward for their contributions, miners receive newly minted bitcoins. This ingenious incentivization system serves as a driving force, motivating participants to contribute their computational capabilities, thus reinforcing the robustness and incorruptibility of the blockchain's security and integrity.&#x20;

The first ever transaction was committed by a pseudonymous “Satoshi Nakamoto” on January 3, 2009. You can find the intricate details of the block that contained this transaction here: [https://blockchair.com/bitcoin/block/0 ](<https://blockchair.com/bitcoin/block/0 >)

Bitcoin as a currency also expounds what the **first generation of blockchain technology** looks like. Knowing the history of the most popular cryptocurrency will help form a very deep understanding of second generation which was brought about by the second-most popular blockchain – **Ethereum**. This entire course is about how Ethereum changed the world one more time over.

## Script

The language that Bitcoin uses to allow developers to carry out all potential functionalities on the Bitcoin blockchain network is significantly different from what the language used in the case of the Ethereum blockchain network (more on this later). The Bitcoin Script is the programming language used for constructing transactions on the Bitcoin network and can help cover activities from sending funds between Bitcoin wallets to creation of multiple accounts. The Bitcoin Script is not a Turing complete language. A Turing- complete system is able to simulate the computational aspects of any possible real-world

system, provided the limitation of finite memory is ignored. This helps the Bitcoin Script to avoid infinite loops or execution scenarios that can consume a huge hash rate and slow down the entire network. This is very different from most of the languages in today’s world (most of which are practically Turing-complete), however, this behavior is what also makes it very secure as there is a hard limit on the kinds of operations it can support.

## The UTXO Model

Bitcoin transactions work by using something called UTXOs (Unspent Transaction Outputs). These are basically **outputs from previous transactions** that can be used as **inputs** in subsequent transactions.&#x20;

This approach towards handling transactions by the Bitcoin network is very different from the State-based model that is used by the Ethereum network.

To understand the UTXO model better, let us take a very simple example. When you visit a shopkeeper and are looking to buy a product worth USD 7, it might mean that you shall provide a USD 10 note to the shopkeeper and receive three USD 1 notes in return. In a nutshell and a very simplistic view, these three USD 1 notes will become outputs that will be available with you to be used as inputs the next time you’re looking to buy something where you could use any of these three notes.&#x20;

However, the technology behind this works slightly differently. In the above example, if it were to happen on the Bitcoin network, each and every output (the USD 1 notes) you would have received would contain a combination of two kinds of scripts – a locking script and subsequently, an unlocking script. The locking script is called **scriptPubKey**, while the unlocking script is called **scriptSig**.&#x20;

The locking script provides necessary conditions to the output which the unlocking script needs to fulfill to be able to use that particular output as an input in a subsequent transaction and be able to “spend it”. Largely, this is how the combination of the locking and the unlocking scripts look:

<figure><img src="/files/9hDGK2c5nTI8VJMexI1m" alt=""><figcaption></figcaption></figure>

As you can see, the unlocking script contains the **digital signature as well as the public key** which are required to be able to unlock the input. The locking script, on the other hand, contains the Bitcoin address or the **public key hash**.


# Ethereum and the State-Based Model

An Alternative to Bitcoin

Throughout the early 2010s, Bitcoin began to gain popularity and soon enough, other blockchains such as Litecoin began to pop up. However, although blockchains like Bitcoin and Litecoin fulfilled their tasks as decentralized payment systems, many in the community began to see perhaps the biggest flaw with such blockchains - the lack of *decentralized programs*.&#x20;

Recall from the [Bitcoin](/introduction/blockchain-theory/bitcoin-and-the-utxo-model) section that each blockchain relies on a virtual machine. Script, the programming language that Bitcoin utilizes, allows developers to write logic which then gets executed on Bitcoin's VM. However, the biggest issue with Script (and the Bitcoin VM as a whole) is that it is catered towards processing the transferring of bitcoin. Bitcoin lacks many features that are required to write sophisticated programs - it is *Turing-Incomplete*.

This is what motivated the creation of Ethereum - to create a blockchain whose virtual machine was general purpose and *Turing-Complete*.&#x20;

## History of Ethereum

## Decentralized Programs


# Remix - A First Glance

Getting Started with Solidity

For aspiring blockchain developers, the first question one might have on their mind is the following: where do I write my smart contracts? For this, we turn to [Remix](https://remix.ethereum.org), an interactive development environment that allows us to both *write* smart contracts and *deploy* our code to EVM-compatible blockchains. For the purposes of this lesson, we will focus on the process of creating and deploying a smart contract.

## Understanding The Structure of Remix

When first loading Remix, you will see a home screen similar to the following:

<figure><img src="/files/4EKe0S7qZwgy7iCnkAh1" alt=""><figcaption><p>Remix Home Screen</p></figcaption></figure>

For right now, what we are interested in is the **Workspaces** column. Remix allows us to create workspaces as a way to separate our projects - imagine putting the smart contracts of hundreds of projects into a single workspace! When first using Remix, we are given a default workspace called *default\_workspace*.&#x20;

Within *default\_workspace*, we are particularly interested in the **contracts** folder. If you click on the **contracts** folder, the following content should drop down from the folder:

<figure><img src="/files/UhReqUMP7XhhHmDChY2e" alt=""><figcaption><p>Contents of the Contracts Folder</p></figcaption></figure>

As the name might suggest, the **contracts** folder is where smart contracts live. This is made evident by the three starter smart contracts that Remix provides us with: *1\_Storage.sol*, *2\_Owner.sol*, *3\_Ballot.sol*. For the purposes of this lesson, however, we will be adding a smart contract to the **contracts** folder.

## Creating Your First Contract

To create a new smart contract in the **contracts** folder, right-click on the contracts folder and the following drop-down menu will appear:

<figure><img src="/files/w2XkSIMKbdNLgijO1QUA" alt=""><figcaption><p>Right-Clicking the Contracts Folder</p></figcaption></figure>

Click on "New File" and you will now be able to define the name of your smart contract file. ***Solidity files have the file extension .sol*****&#x20;.** For the purposes of this lesson, name your file **HelloWorld.sol** .&#x20;

{% hint style="info" %}
How do I know if Remix recognizes my file as being a Solidity file? Any file that is interpreted as a Solidity file has the Solidity logo to the left of it.

![](/files/ipKWPmQ4GlgnpD4fKH2j)
{% endhint %}

Listed below is the source code of **HelloWorld.sol** ; copy-and-paste this into your **HelloWorld.sol** file.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract HelloWorld {

    function print() public pure returns(string memory) {
        return "Hello World";
    }

}
```

## Compiling Your First Contract

Solidity is a compiled programming language and thus, we must compile our smart contracts before deploying them. On the left-hand sidebar in Remix, you will find a button with the Solidity logo on it. Upon clicking the button, you will come across the Solidity compiler that Remix provides to compile our smart contracts.

<figure><img src="/files/6r23Lq31UWPJRljdM9WD" alt=""><figcaption><p>Remix Solidity Compiler</p></figcaption></figure>

To compile **HelloWorld**, simply just click the *Compile HelloWorld.sol* button; if your contract is defined correctly, this will result in a green checkmark appearing next to the Solidity logo. If your contract is not defined correctly, you will not be able to compile your contracts and an error message will appear below the compiler tool.

## Deploying Your First Contract

We've now come to the perhaps the most exciting part - deploying **HelloWorld**! Looking again to the lefthand sidebar, click the button represented by the Ethereum logo which will open up the Remix deployment tool.&#x20;

<figure><img src="/files/ogxVAdirciSHD6yoSxCW" alt=""><figcaption><p>Remix Deployment Tab</p></figcaption></figure>

Assuming **HelloWorld** compiled correctly, this is the tool that we will use to deploy **HelloWorld**. Make sure to select **HelloWorld** as the contract to deploy in the Deployer tool. To deploy **HelloWorld**, simply click the *Deploy* button. For this lesson, you will be deploying to a local blockchain that Remix provides us with (Remix VM).&#x20;

<figure><img src="/files/iGifvaWDKfTu5hl82Sn3" alt=""><figcaption><p>HelloWorld Deployed</p></figcaption></figure>

Congratulations, you have deployed your first smart contract!

## Interacting With Your First Contract

Now that **HelloWorld** lives on your local blockchain, you can use Remix to interact with **HelloWorld**! We will be calling the *print()* function via Remix. Focusing on the Deployer tool, you will see that under the *Deployed Contracts* section, our **HelloWorld** contract has its own tab. Clicking on the tab, you will see the following:&#x20;

<figure><img src="/files/UgYGfLQBzPcKGTcfxGJK" alt=""><figcaption><p>HelloWorld Tab</p></figcaption></figure>

Rather than having to write complicated code to call *print*, Remix allows us to call the function simply by clicking a button. Click on the *print* button and you will see the following:

<figure><img src="/files/eqhQDzBO8UzAMD6QUuSH" alt=""><figcaption><p>Calling print</p></figcaption></figure>

Congratulations, you have interacted with your first smart contract!

## Sources

Remix Documenation: <https://remix-ide.readthedocs.io/en/latest/index.html>


# Hello World!

Writing a Smart Contract That Compiles

To begin our smart contract development journey, we will go from not knowing anything to being able to write a *simple* smart contract *that compiles*.&#x20;

Ambitious developers at first may want to learn how to write smart contracts by examining currently existing smart contracts and getting an idea for what Solidity consists of. While this is certainly a way to go about learning about smart contract development, the sheer complexity of some of the most popular smart contract may be confusing for most to understand. As an example, the code below is for the Uniswap V2 Pair smart contract, which is used for hundreds of thousands of liquidity pools:

{% code fullWidth="false" %}

```solidity
pragma solidity =0.5.16;

import './interfaces/IUniswapV2Pair.sol';
import './UniswapV2ERC20.sol';
import './libraries/Math.sol';
import './libraries/UQ112x112.sol';
import './interfaces/IERC20.sol';
import './interfaces/IUniswapV2Factory.sol';
import './interfaces/IUniswapV2Callee.sol';

contract UniswapV2Pair is IUniswapV2Pair, UniswapV2ERC20 {
    using SafeMath  for uint;
    using UQ112x112 for uint224;

    uint public constant MINIMUM_LIQUIDITY = 10**3;
    bytes4 private constant SELECTOR = bytes4(keccak256(bytes('transfer(address,uint256)')));

    address public factory;
    address public token0;
    address public token1;

    uint112 private reserve0;           // uses single storage slot, accessible via getReserves
    uint112 private reserve1;           // uses single storage slot, accessible via getReserves
    uint32  private blockTimestampLast; // uses single storage slot, accessible via getReserves

    uint public price0CumulativeLast;
    uint public price1CumulativeLast;
    uint public kLast; // reserve0 * reserve1, as of immediately after the most recent liquidity event

    uint private unlocked = 1;
    modifier lock() {
        require(unlocked == 1, 'UniswapV2: LOCKED');
        unlocked = 0;
        _;
        unlocked = 1;
    }

    function getReserves() public view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast) {
        _reserve0 = reserve0;
        _reserve1 = reserve1;
        _blockTimestampLast = blockTimestampLast;
    }

    function _safeTransfer(address token, address to, uint value) private {
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'UniswapV2: TRANSFER_FAILED');
    }

    event Mint(address indexed sender, uint amount0, uint amount1);
    event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
    event Swap(
        address indexed sender,
        uint amount0In,
        uint amount1In,
        uint amount0Out,
        uint amount1Out,
        address indexed to
    );
    event Sync(uint112 reserve0, uint112 reserve1);

    constructor() public {
        factory = msg.sender;
    }

    // called once by the factory at time of deployment
    function initialize(address _token0, address _token1) external {
        require(msg.sender == factory, 'UniswapV2: FORBIDDEN'); // sufficient check
        token0 = _token0;
        token1 = _token1;
    }

    // update reserves and, on the first call per block, price accumulators
    function _update(uint balance0, uint balance1, uint112 _reserve0, uint112 _reserve1) private {
        require(balance0 <= uint112(-1) && balance1 <= uint112(-1), 'UniswapV2: OVERFLOW');
        uint32 blockTimestamp = uint32(block.timestamp % 2**32);
        uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
        if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
            // * never overflows, and + overflow is desired
            price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
            price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
        }
        reserve0 = uint112(balance0);
        reserve1 = uint112(balance1);
        blockTimestampLast = blockTimestamp;
        emit Sync(reserve0, reserve1);
    }

    // if fee is on, mint liquidity equivalent to 1/6th of the growth in sqrt(k)
    function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) {
        address feeTo = IUniswapV2Factory(factory).feeTo();
        feeOn = feeTo != address(0);
        uint _kLast = kLast; // gas savings
        if (feeOn) {
            if (_kLast != 0) {
                uint rootK = Math.sqrt(uint(_reserve0).mul(_reserve1));
                uint rootKLast = Math.sqrt(_kLast);
                if (rootK > rootKLast) {
                    uint numerator = totalSupply.mul(rootK.sub(rootKLast));
                    uint denominator = rootK.mul(5).add(rootKLast);
                    uint liquidity = numerator / denominator;
                    if (liquidity > 0) _mint(feeTo, liquidity);
                }
            }
        } else if (_kLast != 0) {
            kLast = 0;
        }
    }

    // this low-level function should be called from a contract which performs important safety checks
    function mint(address to) external lock returns (uint liquidity) {
        (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
        uint balance0 = IERC20(token0).balanceOf(address(this));
        uint balance1 = IERC20(token1).balanceOf(address(this));
        uint amount0 = balance0.sub(_reserve0);
        uint amount1 = balance1.sub(_reserve1);

        bool feeOn = _mintFee(_reserve0, _reserve1);
        uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
        if (_totalSupply == 0) {
            liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY);
           _mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens
        } else {
            liquidity = Math.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1);
        }
        require(liquidity > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_MINTED');
        _mint(to, liquidity);

        _update(balance0, balance1, _reserve0, _reserve1);
        if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
        emit Mint(msg.sender, amount0, amount1);
    }

    // this low-level function should be called from a contract which performs important safety checks
    function burn(address to) external lock returns (uint amount0, uint amount1) {
        (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
        address _token0 = token0;                                // gas savings
        address _token1 = token1;                                // gas savings
        uint balance0 = IERC20(_token0).balanceOf(address(this));
        uint balance1 = IERC20(_token1).balanceOf(address(this));
        uint liquidity = balanceOf[address(this)];

        bool feeOn = _mintFee(_reserve0, _reserve1);
        uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
        amount0 = liquidity.mul(balance0) / _totalSupply; // using balances ensures pro-rata distribution
        amount1 = liquidity.mul(balance1) / _totalSupply; // using balances ensures pro-rata distribution
        require(amount0 > 0 && amount1 > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED');
        _burn(address(this), liquidity);
        _safeTransfer(_token0, to, amount0);
        _safeTransfer(_token1, to, amount1);
        balance0 = IERC20(_token0).balanceOf(address(this));
        balance1 = IERC20(_token1).balanceOf(address(this));

        _update(balance0, balance1, _reserve0, _reserve1);
        if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
        emit Burn(msg.sender, amount0, amount1, to);
    }

    // this low-level function should be called from a contract which performs important safety checks
    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock {
        require(amount0Out > 0 || amount1Out > 0, 'UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT');
        (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
        require(amount0Out < _reserve0 && amount1Out < _reserve1, 'UniswapV2: INSUFFICIENT_LIQUIDITY');

        uint balance0;
        uint balance1;
        { // scope for _token{0,1}, avoids stack too deep errors
        address _token0 = token0;
        address _token1 = token1;
        require(to != _token0 && to != _token1, 'UniswapV2: INVALID_TO');
        if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens
        if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens
        if (data.length > 0) IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data);
        balance0 = IERC20(_token0).balanceOf(address(this));
        balance1 = IERC20(_token1).balanceOf(address(this));
        }
        uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
        uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
        require(amount0In > 0 || amount1In > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT');
        { // scope for reserve{0,1}Adjusted, avoids stack too deep errors
        uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
        uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
        require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), 'UniswapV2: K');
        }

        _update(balance0, balance1, _reserve0, _reserve1);
        emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
    }

    // force balances to match reserves
    function skim(address to) external lock {
        address _token0 = token0; // gas savings
        address _token1 = token1; // gas savings
        _safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0));
        _safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1));
    }

    // force reserves to match balances
    function sync() external lock {
        _update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)), reserve0, reserve1);
    }
}
```

{% endcode %}

Functions, events, file imports, and other features are only the tip of the iceberg for what the Solidity programming language offers. To ask new students to understand and write a contract like the one above would be outrageous. It is more appropriate, therefore, to learn about smart contracts by understanding how to write the most basic of smart contracts. Although mundane at first, learning about *elementary* smart contracts will allow us to learn about more advanced features later on.&#x20;

### Resources

Uniswap V2 Pair Contract Source Code: <https://github.com/Uniswap/v2-core/blob/master/contracts/UniswapV2Pair.sol>


# Solidity File Structure

What is a Solidity File?

As mentioned in [Remix - A First Glance](/introduction/remix-a-first-glance), all Solidity file have the file extension **.sol** . However, we have not yet covered what a Solidity file actually contains. In this section, we will go over *the general structure* of a Solidity file.&#x20;

Most Solidity files contain the following components (in order):

1. SPDX License Identifier
2. Solidity Version
3. File Imports
4. Contracts

### SPDX License Identifier

The *Software Package Data Exchange (SPDX) License Identifier* is commonly found at the top of most smart contracts. Although not required, many compiler (such as the Remix compiler) will give you warnings if your Solidity files do not include a SPDX license identifier.

Smart contract code, like all other digital content, are subject to copyright laws. Rather than having to go through the hassle of contacting the developer of a smart contract which you wish to use, the SPDX license identifier communicates to all potential users the manners if which one is allowed and is not allowed to use said smart contract code.&#x20;

The **MIT** license identifier is one of the most commonly used SPDX license identifiers. Looking at the license itself, we can see that any contract with the **MIT** license has almost no limitations to its use:

> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Not all smart contracts can be used freely, however. A famous case of this is with the Uniswap V3 contract code; being protected under the BSD-1.1 license, users were not able to utilize the code in a production/commericial setting (i.e. deploying Uniswap V3 on other blockchains) until April 1st, 2023.&#x20;

### Solidity Version

Due to there being multiple versions of Solidity, following the SPDX License Identifier, we must declare the version of Solidity which we wish to use to compile our file. The Solidity version declaration is as follows:

```solidity
pragma solidity 0.8.0;
```

In the above code snippet, we are telling our compiler that we wish to use Solidity version 0.8.0. However, we are not restricted to using solely a single version. Consider the following:

```solidity
pragma solidity ^0.8.0;
```

In contrast to the first code snippet, the second code snippet tells the compiler that we wish to compile using any version of Solidity greater or equal to version 0.8.0. An implicit restriction imposed by the caret symbol is that we wish to compile with versions of Solidity with the prefix 0.8. As an example, if we specify the compiler version of our Solidity contract to be `^0.7.0`, we *cannot compile* with `0.6.*` compiler, `0.8.*` compiler, etc. This is because between different major versions (i.e. `0.x.*`, `0.y.*` where `x != y`), there exists incompatible differences.&#x20;

Furthermore, we can also declare a range of versions for which we want our contract to compile to; an example of this is as follows:

```solidity
pragma solidity >=0.7.6 < 0.8.17;
```

Here, we are telling the compiler that we wish to compile this contract with any version of Solidity greater than or equal to version `0.7.6` and less than `0.8.17`.

{% hint style="info" %}

#### Aren't We Combining Incompatible Solidity Versions?

The statement `pragma solidity >=0.7.6 < 0.8.17;` might seem to contradict the earlier statement about mixing major versions. However, it is implicitly assumed that our contract will be compiled with any version within the range that *does not break its functionality*.&#x20;
{% endhint %}

### File Imports

Like with most other programming languages, we can divide up our code into different files in order to avoid merging our code into a single file. Assume the directory we are working in is as follows:

```
> HelloWorld.sol
> HelloMoon.sol
```

If we are working with `HelloWorld.sol,` and *we wish to import* `HelloMoon.sol`, we can write the following in `HelloWorld.sol` :

```solidity
import "./HelloMoon.sol";
```

In practice, we sometimes might prefer to inherit only certain objects (i.e. contracts, interfaces, libaries, etc.) from a file. Assume the following is the contents of `HelloMoon.sol`:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract A {}

contract B {}
```

If we wished to only import contract `A`, we would write the following in `HelloWorld.sol`:

```solidity
import {A} from "./HelloMoon.sol";
```

### Contracts

This section is self-explanatory; this is where your smart contracts will live.

### Resources

SPDX Tutorial: <https://github.com/david-a-wheeler/spdx-tutorial/blob/master/README.md#spdx-license-identifiers>

MIT License: <https://opensource.org/license/mit/>

Uniswap V3 BSD-1.1 License: <https://support.uniswap.org/hc/en-us/articles/14569783029645-Uniswap-v3-Licensing->

Solidity Docs: <https://docs.soliditylang.org/en/latest/layout-of-source-files.html>


# Primitive Values & Types

What are we working with?

Although this course is primarily focused on developing smart contracts, it is crucial to understand the values that we will be working with; after all, code is just a collection of expressions that all add up to something meaningful.

Being a statically-typed programming language, Solidity requires us to declare the types of each value we utilize in our smart contracts. This section will therefore focus on the primitive types that Solidity offers.

### Primer on Integer Sizes

Before discussing any types, it is important to discuss the concept of integer sizes (or to be mathematically correct, the *magnitude* of integers)

Languages like Python make it seem as if we are able to store arbitrarily large integers with no limitations. However, developers who use languages such as Rust or C will know that this is not the case. Integer types in most programming languages are restricted to a certain *range*; this is due to the fact that we are unable to store infinitely large integers (as this would imply that we have infinitely large memory).&#x20;

What defines the range of integer types? Recall that all integers can be written in binary form. Listed below are integers (represented in decimal format) and their associated binary representation:

```
19 => 10011
432 => 110110000
5 => 101
```

The binary representation of the integers above consist of bits (i.e. 0s or 1s). It is bits that are used to define the range of integer types. As an example, consider a 32-bit integer; the 32-bit part refers the fact that the integer can only store integers whose binary representation consists of *at most 32 bits.*

From a mathematical perspective, a `n`-bit integer means that any integer `x` within the range `0 <= n <= 2**n - 1` can be stored.

{% hint style="info" %}

#### What About *Signed* Integers?

Signed integers are represented in binary via the two's complement formula. Although we won't get into how to compute the binary representation of a signed integer, note that a `n`-bit signed integer means that any integer `x` within the range `-2**(n-1) <= x <= 2**(n-1) - 1` can be stored
{% endhint %}

### Integers (Signed and Unsigned)

In Solidity, the two *general* integer types available to us are `uint` (unsigned integer) and `int` (signed integer). Both `uint` and `int` are of size 256-bits. However, these are not the only integer types that we are restricted to; in fact, we can utilize any signed/unsigned integer type of size `n` where `n` is a multiple of 8. The following is a list of valid integer types in Solidity:

```
uint8, uint16, uint24, uint32, ..., uint248, uint256
int8, int16, int24, int32, ..., int248, int256
```

#### Integer Operations

Assuming `a`, `b` are integers and `c` is a non-zero integer, listed below are the operations that we can perform on integers in Solidity

```solidity
a + b; // Addition
a - b; // Subtraction
a * b; // Multiplication
a / c; // Division
a % c; // Modulo
a ** b; // Exponentiation
```

### &#x20;Booleans

Solidity provides us with the boolean values `true` and `false`.&#x20;

### Address

Since addresses are commonly used within smart contracts, Solidity provides us with an address type. An example of the address type is as follows:

```solidity
address myAddress = 0x3D2A511EbB0f53337749B4bd758feF2658f7E69e;
```


# Contract Structure

What is a Contract?

Having now understood the layout of a Solidity file and values, we will tackle the concept of the smart contract itself. While there are many different ways to approach learning about the structure of a smart contract, we will approach it from the perspective of *object-oriented* programming; as it will become evident later on, Solidity itself is an object-oriented programming language (perhaps one can call it *contract-oriented programming*)

### A Primer on Object-Oriented Programming

Object-oriented programming is defined by... well, objects. In particular, developers of object-oriented programming lanugages are able to leverage *classes* - blueprints that one can *instantitate* to create *objects*.&#x20;

A great example of understanding object-oriented programming is by looking at dog breeds. Consider the Poodle; if we consider poodles to be a *class*, then any living poodle can be consider *an instance* of the Poodle class.

<div align="center"><figure><img src="/files/z94MuKUqgfTua90Yn6qo" alt=""><figcaption><p><a href="https://www.animalblueprintcompany.com/product/poodle/">Source</a></p></figcaption></figure> <figure><img src="/files/st27zQUQygjWiUykDMpu" alt=""><figcaption><p>Lucho, my pet poodle</p></figcaption></figure></div>

Approaching object-oriented programming from a technical perspective, classes are defined by the following two characteristics:

* Fields: the *state* of objects
* Methods: the *behavior* of objects

### Contracts and Classes

Contracts, like classes in object-oriented programming, consist of both *fields* and *methods*. In practice, the fields of smart contracts come first and is followed by the contract methods.

```solidity
contract Rectangle {

    // Contract Fields
    uint256 length;
    uint256 width;
    
    // Contract Methods
    function compute() public pure returns(uint256) {
        return length * width;
    }

}
```

Above, we have a contract called **Rectangle** which represents... a rectangle. All contracts start off with a *contract header*, which, in the case of **Rectangle**, is defined by `contract Rectangle`. Following the contract header, we have the definition of **Rectangle** enclosed in curly brackets; this is where we will find the contract fields and methods.&#x20;

Contract fields are *stateful*; as an example, if we deployed an instance of the **Rectangle** contract, its associated `length` and `width` fields would forever persist on the blockchain, and any changes to said fields would be peristed as well.

Contract methods are the specified *behavior* of smart contracts; in the case of **Rectangle**, the function `compute()` calculates the area of the rectangle contract.&#x20;

{% hint style="info" %}

#### State Variables vs Memory Variables

Contract fields are more commonly known as *state variables* (i.e. they are persistent variables associated with a contract's state). Variables that are marked as `memory` or are *allocated* via the `new` keyword are known as *memory variables* (and are found within function bodies).
{% endhint %}


# Functions

The Behavior of Smart Contracts

We saw in the [Contract Structure](/introduction/hello-world/contract-structure) section that methods are what allows for contracts to be decentralized programs that they are. However, we have yet to explore what methods (functions) consist of.

### Function Headers

Although one may want to dive right into the logic of a function itself, an equally important task is defining the function header itself. In Solidity, all function headers begin with the `function` keyword which tells the compiler that we are defining a function. Afterwards, we declare the name of the function itself, followed by brackets

```solidity
function print()
```

In the code snippet above, we are defining a function named `print`.&#x20;

{% hint style="info" %}

#### What About Function Parameters?

All function parameters live within the brackets next to the function name. As an example, if we wanted `print` to take in an unsigned integer, we would modify our function header to the following:

`function print(uint num)`&#x20;

If we wanted our function to take in *multiple* parameters, we would modify our function header as follows:

`function print(uint numOne, int numTwo)`
{% endhint %}

After the `function` keyword, function name, and optional parameters, the last necessary section of the function header which we must define is the *visibility* of the function.

All smart contracts live in a distributed system, and one of the challenges this presents is how to make sure only certain actors can call certain functions. For this, Solidity introduces the concept of *visibility*; listed below are the four types of visibility available to functions:

* **Public**: any actor can call a public function
* **Private**: only the contract itself can call a private function
* **Internal**: only the contract itself and any child contract can call an internal function
* **External**: the contract itself and child contracts cannot call an external function; other actors can call external functions.

Below, we see visibility being used in (syntactically correct) function headers:

```solidity
function print() public {}
function print() private {}
function print() internal {}
function print() external {}
```

### Extensions of Function Headers

All function headers are required to have the `function` keyword, a function name, and a specified visibility. However, the following questions demonstrate what our current understanding of function headers is missing:

* How do we define return values?
* What if we can guarantee that our function does not modify or even view the state of the blockchain?

We begin by tackling the idea of defining return values. At the end of the function header, if we want our function to return a value, we append the `return` keyword to the function header and in parentheses, declare the return types of the values the function will return. An example of returning values from a function can be seen below:

```solidity
function getNum() public returns (uint) {}
```

Like function parameters, we are not limited to just a *single* return value; functions in Solidity are capable of returning *multiple* values:

```solidity
function getNums() public returns (uint, int) {}
```

Focusing now on the *mutability guarantees* of a function, we can add mutability keywords to a function header if a function meets certain criterias:

* **View**: the function at most *only views* the state of the blockchain
* **Pure**: the function *does not view* the state of the blockchain *at all*

As an example, consider the **Rectangle** contract from [contract structure](/introduction/hello-world/contract-structure):

```solidity
contract Rectangle {

    uint256 length;
    uint256 width;

}
```

Now consider the following two functions we wish to add to **Rectangle**:

* *getFive*: returns the number 5
* *getArea*: returns the area of the rectangle

Notice that in the case of *getFive*, we can declare this function as being `pure` since this does not require us to view the state of the blockchain in any manner. For *getArea*, we can mark it as `view` since we are viewing the state of the blockchain (via using the fields `length` and `width`) but we cannot mark it as `pure`:

```solidity
contract Rectangle {

    uint256 length;
    uint256 width;
    
    function getFive() public pure returns (uint256) {
        return 5;
    }
    
    function getArea() public view returns (uint256) {
        return length * width;
    }

}
```

One interesting observation is that *all* `pure` functions can be marked as `view`, but *not all* `view` functions can be marked as `pure`.&#x20;

### Function Body

The function body is where the logic of a function lives. Like state variables, each line within the function body *must* end with a semicolon.


# Data Structures

Helping Us Store Data

Any programming language would not be complete without the inclusion of *data structures*; in Solidity, we are provided with the following data structures:

### Mappings

Mappings can be thought of as dictionaries, where we are mapping keys to values. The syntax for mappings are as follows:

```solidity
mapping(keyType => valueType) mappingName;
```

As an example, if I wanted to create a mapping called *ages* which mapped users (represented by their addresses) to their age, I could create the following mapping:&#x20;

```solidity
mapping(address => uint) userAges;
```

Perhaps one of best known use cases of mappings is for tracking the ERC20 token balances of user within a smart contract. Listed below is an *elementary* ERC20 contract which shows this off:

```solidity
contract BabyERC20 {

    // Tracks balances of token holders
    mapping(address => uint) balances;

}
```

Assuming `map` is a mapping, the following code below demonstrates how to assign a key-value pair within a mapping:

```solidity
map[key] = value;
```

{% hint style="info" %}

#### Restrictions on Key/Value Types

As noted in the Solidity documentation, there are restrictions regarding what types keys can be. The types that are not allowed to be used as keys are *mappings, structs, and arrays*. Furthermore, any user-defined types are *not* allowed to be used as key types.
{% endhint %}

One note about mappings is that they can only be used as state variables. In other words, one cannot create a new mapping within a function; one can only use a mapping associated with a state variable within a function.

#### Aside: Default Values

In languages like Python, we run into exceptions whenever we try to query an undefined key-value pair. An example is as follows:

```python
>>> x = {}
>>> x["key"]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'key'
```

In Solidity, however, we are able to query undefined key-value pairs (assuming we obey relevant type rules). An interesting question, then, is *what values are returned when querying an undefined key*? For any undefined key, the value returned is the *default value* for the value type. As an example, assume we have the following (empty) mapping:

```solidity
mapping(uint => uint) map;
```

If we try to query the value paired to key `7`, we will get the value `0`. If we try to query any key in `map`, we will always get the value `0` in return. Listed below are the default values for some types:

* `uint`/`int`: `0`
* `bool`: `false`

### Static Arrays

Static arrays are arrays whose size is defined at creation time. The syntax for creating an array **in memory** is as follows:

```solidity
T[] arrayName = new T[](n);
```

where `T` is the type of the array and `n` is the size of the array. In Solidity, all elements of an array must be of the same type. Another way of creating an array in memory is as follows:

```solidity
T[n] arrayName;
```

As the name might imply, we cannot change the size of a static array once initialized. If you wish to have flexibility with the size of your array, consider using a dynamic array.

To create a state variable of the static array type, we can again use the folowing syntax:

```solidity
T[n] arrayName;
```

In the case that we know how to **define** our static array, below is an example of how we can declare and define a static array at the same time (this holds for both memory and storage):

```solidity
uint[2] lst = [1, 2];
```

#### Nested Arrays

In certain situations, it may be necessary to use a nested array. Solidity allows us to create *static nested arrays* as follows:

```
T[n][m] nestedArrayName;
```

where `T` is the type of the array, and `n`, `m` are the 1st and 2nd dimension sizes of the nested array respectively.

#### Accessing Array Elements

Below is an example of how to access an element of an array:

```solidity
lst[0];
```

### Structs

Structs are data structures that can be used to hold several variables in one place; below is an example of structs in the C++ programming language:

```cpp
struct {
    string name;
    int age;
    bool isAdult;
} Person;
```

In Solidity, we can create structs as follows:

<pre class="language-solidity"><code class="lang-solidity"><strong>struct Person {
</strong>    string name;
    int age;
    bool isAdult;
}
</code></pre>

Below is an example of how to create a struct in memory and how to access an element of a struct:

```solidity
Person memory p = Person("Rodrigo", 21, false);
p.age;
```

### Enums

Enums, or enumerations, allow us to group 'constants' without having to explicitly declare them.

Consider a contract where we want to organize students by their year. If we wanted to map students (represented by their addresses) to their year, we could use integers to represent their year, as seen below:

```
0 - Freshman
1 - Sophomore
2 - Junior
3 - Senior
```

However, keeping track of these constants manually introduces the possibility of bugs. Enums allow us to use these constants without explicitly using their values:

```solidity
enum Year {
    Freshman, // 0
    Sophomore, // 1
    Junior, // 2
    Senior // 3
}
```

{% hint style="info" %}

#### Enum Values

The elements of an enum are associated with an integer determined by their position. For example, the first element is equal to 0, the second element is equal to 1, and so on.
{% endhint %}

Below is an example of a contract which uses the `Year` enum to keep track of students:

```solidity
contract Cornell {

    enum Year {
        Freshman, 
        Sophomore, 
        Junior, 
        Senior 
    }

    mapping(address => Year) students;

    function admitStudent(address student) public {
        students[student] = Year.Freshman;
    }

}
```

The function `admitStudent`, in particular, takes the address of an admitted students and maps them to the `Freshman` value.

### References

Solidity Types: <https://docs.soliditylang.org/en/v0.8.7/types.html#>

Solidity Expressions and Control Structures: <https://docs.soliditylang.org/en/latest/control-structures.html#>


# Summary & Exercises

Congratulations, you have learned the basic Solidity syntax required to write smart contracts!

### Exercises

*


# Hello World! Pt. 2

Smarter Smart Contracts

In [Hello World!](/introduction/hello-world), we went from not being able to write a single line of Solidity to creating a fully-functioning smart contract.  We even got to see some of the data structures that Solidity provides for us. Even with this in mind, however, below are some questions that expose our gaps in knowledge regarding writing smart contracts in Solidity:

* How do we write logic that *isn't* sequential?
* How do we allow *only certain actors* to interact with our contracts?
* Where the strings at?
* How can we avoid repeating code in our contracts?

In this section, we will work on answering these questions and many more. At the end, you will have learned most of the Solidity syntax; furthermore, many of the popular smart contracts that might have seem confusing to you at first will finally begin to make sense (or at least partially).


# Control Flow

Logic is no longer sequential!

Control flow statements allow for our smart contracts to execute certain logic repeatedly or to execute only a certain block of logic. &#x20;

### If/Else If/Else Statements

The general structure for *if/else if/else structures* is as follows:

```solidity
if (E) {
    // Insert logic here
} else if (E) {
    // Insert logic here
} else {
    // Insert logic here
}
```

where `E` is a *boolean expression.*

### While Loops

The general structure for `while` loops is as follows:

```solidity
while (E) {
    // Insert logic here
}
```

where `E` is a *boolean expression*.

### Do While Loops

In contrast to while loops, *do-while loops* executes the logic contained within the loop body first before checking if the loop condition holds. As an example, consider the following psueodocode:

```
x <- 0
do
    x <- x + 1
while x < 0
```

After `x` is initialized to `0`, we run one iteration of the do-while block and then check afterwards whether `x < 0`. Although the loop condition was never satisfied in the first place, we still end with `x = 1`.

In Solidity, do-while loops are as follows:

```
do {
    // Insert logic here
} while (E)
```

### For Loops

Finally, we will cover *for loops*. The syntax for loop variables is as follows:

```solidity
for (A; B; C) {
    // Insert logic here
}
```

where `A` is the loop variable, `B` is the loop condition, and `C` is the loop updater. An example of a for-loop in Solidity is as follows:

```solidity
uint x = 0;
for (uint i = 0; i < 5; i++) {
    x += 1;
}
```

{% hint style="info" %}

#### Using `uint` As The Loop Variable Type

Although not required, using `uint` as the loop variable type brings the advantage of being able to index arrays without having to do any sort of explicit type conversions.
{% endhint %}

### Aside: `break` and `continue`

At times, we may not want for our loops to continue executing until termination. For this, Solidity provides us with the following keywords:

* `break`: exit out of the loop *completely*
* `continue`: move onto the next iteration

Examples of these keywords are as follows:

```solidity
while (E) {
    if (E') {
        break;
    }
}

for (uint i = 0; i < 5; i++) {
    if (E) {
        continue;
    }
}
```

where `E`, `E'` are arbitrary *boolean expressions*.&#x20;


# Interfaces and Inheritance

B is A

As we have seen in previous sections, Solidity is an object-oriented programming language which allow us to create classes (contracts) and objects (deployments of contracts). However, object-oriented programming offers much more than this. For example, one of the features that OOP provides is inheritance; classes whose logic can be derived from other classes.&#x20;

Solidity, in alignment with OOP, also allows us to utilize inheritance with respect to contracts. Consider a situation where we have two contracts `A` and `B`, and we want for B to inherit the state/logic of A. Below is the syntax of how we would do that in Solidity:

```solidity
contract A {}

contract B is A {}
```

To demonstrate the inheriting property of smart contracts, consider the following code:

```solidity
contract A {

    uint age = 21;
    
    function getName() public pure returns(string memory) {
        return "A";
    }

}

contract B is A {

    function getAge() public view returns(uint) {
        return age;
    }

}
```

In contract `A`, we have the state variable `age = 21` and the function `getName`, which returns the name of the contract of the contract. When we create `B`, `B` inherits the function `getName`. Furthermore, `B` also inherits the state variable `age`, which is made evident by the function `getAge`.

### Overriding Functions

Although inheritance allows us to extract common logic to a single smart contract, there are instances where we might want to define our own logic for a function rather than using the implementation provided by the parent function.&#x20;

To *override* a function, our first intuition might be as follows:

```
contract A {

    function getName() public pure virtual returns(string memory) {
        return "John";
    }

}

contract B is A {

    function getName() public view returns(string memory) {
        return "Bob";
    }

}
```

However, if you attempt to compile this the code above, you will get a *very angry message* from the compiler telling you that you need to *declare* `getName` *to be override-able*. And indeed, this is what we're forgetting:

{% code lineNumbers="true" %}

```solidity
contract A {

    function getName() public pure virtual returns(string memory) {
        return "John";
    }

}

contract B is A {

    function getName() public view override returns(string memory) {
        return "Bob";
    }

}
```

{% endcode %}

Examining the code above on a line-by-line basis:

* Line 3: we mark `getName` in the parent contract as `virtual`: this tells the compiler that children contracts can override the logic of `getName`
* Line 11: we mark `getName` in the child contract as `override`: this tells the compiler that we are *overriding* the logic defined in the parent contract with our own

### The `super` Keyword

There exists instances where, although we want to rely on the logic of a parent contract, we also want to extend the functionality of a function without having to rewrite code. The following example makes this dilemma clear:

> Everyone who works at company A is an employee by default. Furthermore, everyone at company A receives a salary of $100,000. However, the CEO, in addition to already being an employee by default, also receives a bonus of $25,000.

In the example above, if we represent employees and the CEO as contracts, the associated code is as follows:

{% code lineNumbers="true" %}

```solidity
contract Employee {

    uint salary;

    function setSalary() public virtual {
        salary = 100000;
    }

}

contract CEO is Employee {

    uint bonus;

    function setSalary() public override {
        salary = 100000;
        bonus = 25000;
    }

}
```

{% endcode %}

Notice that in line 16, we are copying the same code as in line 6 (i.e. we are duplicating code). Rather than duplicating code, which can be a source of bugs later on, we can utilize the `super` keyword to reuse the logic of the parent implementation.

{% code lineNumbers="true" %}

```solidity
contract Employee {

    uint salary;

    function setSalary() public virtual {
        salary = 100000;
    }

}

contract CEO is Employee {

    uint bonus;

    function setSalary() public override {
        super.setSalary();
        bonus = 25000;
    }

}
```

{% endcode %}

In line 16, we are calling the `setSalary` which pertains to the parent contract (i.e. we are setting the salary of the CEO). Afterwords, in line 17, we are setting the bonus of the CEO to be equal to `25000`.

### Overloading Functions

Consider the following code:

```solidity
contract A {

    function returnArg(string memory name) public pure returns(string memory) {
        return name;
    }

}

contract B is A {

    function returnArg(uint num) public pure returns(uint) {
        return num;
    }

}
```

Both contract `A`, `B` contain the function `returnArg` which returns the argument passed in. However, `A`'s implementation of `returnArg` deals with strings while `B`'s implementation of `returnArg` deals with uints. With our current understanding of functions and inheritance, this shouldn't compile, right?

It turns out, however, that this is completely valid Solidity code. The reason for this is that although both implementations of `returnArg` are different, the `returnArg` found in `A` is actually not the same function as `returnArg` in `B`.&#x20;

To understand why this is the case, we must introduce the idea of function selectors - the ID of a function. Although we will dive much deeper into this when we discuss the EVM, what you need to know is that when compiled, each function name is associated with an number. How is this number generated? The ID of a function is dependent on the type function parameters and the ordering of the parameters. As an example, the following demonstrates this idea firsthand:

```
returnArg(uint age) => i
returnArg(uint year) => i
returnarg(int age) => j
returnArg(uint age, string memory name) => k
returnArg(string memory name, uint age) => m
```

As it might have become evident, there is no need to *override* `returnArg` because `returnArg` in contract `A` has a different function selector than `returnArg` in contract `B`. In this scenario, we are *overloading* `returnArg` with two different implementations.

### Interfaces

In object-oriented programming languages, a way in which to hide implementation details from users of a class is via the use of interfaces. Likewise, in Solidity, we can also utilize interfaces to communicate to users the functionality of any inheriting smart contract. The syntax of a contract interface is as follows:

```solidity
interface interfaceName {}
```

To motivate our understanding of interfaces, consider the following smart contract:

```solidity
contract Miner {

    bool isWorking;
    
    function clockIn() public {
        isWorking = true;
    }
    
    function clockOut() public {
        isWorking = false;
    }
    
    function mine() public {}

}
```

If we only wanted to understand the behavior of the `Miner` contract rather than the implementation details, we could write an interface for the `Miner` contract:

```solidity
interface IMiner {

    function clockIn() external {}
    
    function clockOut() external {}
    
    function mine() external {}

}
```

There's a couple of things to note from this example:

* Why are all functions marked as external? Recall that external functions can only be called by other accounts. Therefore, by marking all functions in an interface as `external`, we are signaling that the functions a contract implements via an interface are meant to be called by other accounts.
* Although all functions are marked in an interface as `external`, contracts that implement an interface can change the visibility of said functions as `public`. However, they cannot be changed to `private` or `internal`.

### Abstract Contracts

In our final section on inheritance, we cover the concept of *abstract contracts* - contracts where there is at least one function that is missing implementation details.&#x20;

Consider the following scenario where we wish to implement a Mathematician contract; although all chefs are required to remember a specific equation (in this case, the average of two numbers), they are also required to remember their own specific equation. Already, it is obvious that there will be some overlapping logic (i.e. all chefs will have a function to compute the average of two numbers), there will also be mutually exclusive logic in each mathematician contract.&#x20;

```solidity
interface IMathematician {

    function computeAverage(int x, int y) external {}
    
    function computeSpecialEquation() external {}

}
```

Although the `IMathematician` interface tells us about the behavior of a mathematician, it still does not allow us to implement the overlapping logic that all mathematicians have. In this case, it would be best to use an abstract contract. The syntax for an abstract contract is as follows:

```solidity
abstract contract abstractContractName {}
```

The code below shows how we can use abstract contracts to create our `Mathematician` contract:

```solidity
abstract contract Mathematician {
    function computeAverage(int x, int y) public {
        return (x + y) / 2;
    }
    
    function computeSpecialEquation() public {}
}
```


# Constructors

Defining Initial Values

As with other object-oriented languages, constructors allow us to define the values of state variables at contract deployment time. The syntax for defining a contract constructor is as follows:

```solidity
constructor() {}
```

To motivate our understanding of constructors in Solidity, let's try creating a student contract whose values will be defined at deployment-time

```solidity
contract Student {

    uint256 age;
    string name;
    
    constructor(uint studentAge, string memory studentName) {
        age = studentAge;
        name = studentName;
    }

}
```

In the example above, our constructor takes in two arguments: `studentAge` and `studentName`. These two arguments are then used to *define* the state variables `age` and `name`. Note that when defining state variables in constructors, said state variables *must already be declared* in the contract body (i.e. not within a function body). As an example, the following code would not compile because `age`, although declared within the contract constructor, was declared as a local variable.

```solidity
contract Student {

    constructor(uint studentAge, string memory studentName) {
        uint256 age = studentAge;
        string memory name = studentName;
    }

    function getAge() public view returns(uint256) {
        return age;
    }

}
```

### Calling Parent Constructors

In the Interfaces and Inheritance section, we saw that child contracts can call the functions of parent contracts via the `super` keyword. Child contracts are able to call the constructor of parent contracts via two different methods.

Focusing first on the constructor function itself, the syntax for calling a parent constructor is as follows:

```solidity
constructor() parentConstructor() {}
```

As an example, if we are defining the constructor of contract `B`, and we wish to call the constructor of parent `A`, we can write the following:

```solidity
contract A {

    constructor(uint num) {}

}

contract B is A {

    constructor(uint num) A(num) {}

}
```

Notice that when calling parent constructors, we can pass in the argument values of the child constructors; in the example above, we pass in the parameter `num` as the argument for the constructor of `A`.

The other way to call a parent constructor focuses on the contract header; the syntax for this is as follows:

{% code lineNumbers="true" %}

```solidity
contract A {

    constructor(uint num) {}

}

contract B is A() {

    constructor(uint num) {}

}
```

{% endcode %}

As seen above, in line 7, we are calling the constructor of parent contract `A`. One thing to note though is that for parent constructors called in the child contract header, we cannot pass in arguments from the child constructor (i.e. the values passed to the parent constructor must be *explicitly declared*). As an example, the following code would not compile:

```solidity
contract A {

    constructor(uint num) {}

}

contract B is A(num) {

    constructor(uint num) {}

}
```

{% hint style="info" %}

#### Order of Constructors

If a child contract inherits multiple contracts, then the order in which the constructors of the parent contracts are called (regardless of how a user orders them in the constructor header) is defined by the order in which the contracts are inherited by the child contract.\
\
As an example, if we have:\
`contract C is A, B`

then the constructor of `A` is always executed before the constructor of `B`. Therefore, the following code is equivalent:\
`constructor() A() B() {}`

`constructor() B() A() {}`
{% endhint %}

### Resources

* <https://www.geeksforgeeks.org/solidity-constructors/>


# Contract Interactions

Our Contracts Are No Longer Sandboxed!

While we are beginning to write more sophisticated smart contracts, the one thing that holds back our contracts is the inability to *interact* with other smart contracts. Almost every popular contract on EVM-compatible chains contain logic which allows it to call the functions of other smart contracts.&#x20;

To understand how contract interactions might look like, consider the following examples:

* **Uniswap**: Assume you place a trade which swaps USDC for WETH on [app.uniswap.org](https://app.uniswap.org/). Your trade is first sent to the Uniswap V3 [SwapRouter.sol ](https://github.com/Uniswap/v3-periphery/blob/main/contracts/SwapRouter.sol)which then calls the relevant USDC-WETH pool contract necessary to conduct the desired swap.
* **MultiSig Wallet**: All multi-signature wallets (multisigs) on Ethereum and similar chains are smart contracts. Therefore, contract-to-contract interactions are an inherent feature of multisigs.

### Primer: Address Type Methods

Before discussing the logistics on contract interactions, we will first discuss the types that allow us to call other contracts in the first place - the address type and *contract* types.

The address type, as previously discussed in [Primitive Values & Types](/introduction/hello-world/primitive-values-and-types), represents the addresses of accounts. What is unique about the address type is that it comes with built-in methods that allows us to interact with values of the address type.

To motivate our understanding, consider the following variable:

```solidity
address vitalik = 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045;
```

As the name might suggest, this is the address of Vitalik Buterin's Ethereum account. To get an idea of the functions that the address type provide, we can, as an example, get the balance of Vitalik's account.

```
vitalik.balance;
```

At the time of writing this, `vitalik.balance` returns `4119625876629850684131`, which is the amount of Wei in his account.

### Contract Types and Calling Other Contracts

The address type provides many functions that we will soon see, but with our current understanding of Solidity, it is useful mainly when dealing with EOAs since there is no *nice* way to call functions of contracts under the address type. Thankfully, Solidity provides us with *contract* types which allow us to treat contracts like their own types. To understand what we mean by contract types, consider the following code:

```solidity
contract A {

    uint num = 5;

    function getNum() public view returns(uint) {
        return num;
    }

}
```

Furthermore, let's assume that address that contract `A` is deployed at is as follows:

```solidity
address contractAccount = 0x2D4d2d096375FCc1503DA59c5B7FB4f69EAdf1e3;
```

Our goal is to be able to call the `getNum` function of the contract located at `contractAccount`. Ideally, we want to be able to call `getNum` directly; using contract types, we first *convert* `contractAccount` to type `A`:

```solidity
A convertedContract = A(contractAccount);
```

Having now defined `convertedContract`, we can now call `getNum` as follows:

```
convertedAccount.getNum();
```

Since contract types are types, we can use them wherever regular types are also used. An example of this can be found below:

```solidity
contract A {

    function getFive() public pure returns(uint){
        return 5;
    }

}

contract B {

    function callGetFive(A contractToCall) public pure {
        contractToCall.getFive();
    }

}
```

### Sending Ether

While we are now able to call the functions of other smart contracts, there is still one question remaining: how can we send ether to other contracts?

It is trivially known that users can send ether to each other. Since contracts are also accounts, we can send ether to and from contracts as well. In this last section, we will cover how to write code that allows our contracts to send ether to other Ethereum accounts and how to specify our functions to be able to receive ether.

#### The `call` Function

Assume we want to send `x` ether to an account (EOA or contract) with address `y`.  Assuming that the contract we are sending from has enough ether, the syntax below demonstrates how we can send ether via smart contracts:

```solidity
y.call{value: x}("");
```

The `call` function is available to all values of type address. Although we will see later in the course that `call` is a powerful function, for our current purposes, we will treat `call` as a function that allows us to send ether to any account.

There are two types of parameters that are used in the `call` function:

* *Special Parameters*: consists of the `value` field and the `gas` field. `value` is the amount of wei we are sending to the account, while `gas` specifies the maximum amount of gas that can be used when calling the account.
* *Data Parameter*: consists of a hexadecimal value to be sent when calling the account

Note that special parameters go inside curly brackets and use colon-notation, while the data parameter goes in rounded brackets and uses equality-notation.

The `call` function, furthermore, returns two values; these values are as follows:

```solidity
(bool success, bytes memory data) = y.call{value: x}("");
```

where `success` indicates where the call function was successful (i.e. whether the transfer of ether was successful) and `data` is the data returned from the `call` function.

{% hint style="info" %}

#### Checking for Success

Although a successful `call` function does not imply that the logic that we intended to occur occurred, it is *always* a good idea to check that the `success` return value of a `call` function is equal to `true`.
{% endhint %}

#### The `Payable` Keyword

Now that we know how to program our smart contracts to send ether to other accounts (EOAs or contracts), its natural to ask whether if we can include ether as part of function calls. To motivate our curiousity, consider the following example:

{% code lineNumbers="true" %}

```solidity
contract Club {

    mapping(address => bool) isMember;

    uint membershipFee = 1;

    function becomeMember() public returns(bool) {
        require(msg.value == 1, "Did not pay exact fee!");
        isMember[msg.sender] = true;
        return true;
    }

}
```

{% endcode %}

The contract above represents a club where anyone can obtain membership if they pay the require 1 wei membership fee. Examining our contract line-by-line, we have the following:

* Line 8: we are checking if the user has sent the required 1 wei with the function call
* Line 9: we are setting the user's membership status to true
* Line 10: we are return `true` (indicating to the caller that the membership was successfully obtrained)

To send ether alongside a contract call, we again can attach special parameters like we did for the `call` function; the syntax for attaching special parameters in the case of the `becomeMember` function is as follows:

```solidity
becomeMember{value: 1}();
```

Tying this all together, the following code example consists of the `Club` contract and a `User` contract which applies for club membership:

```solidity
contract Club {

    mapping(address => bool) isMember;

    uint membershipFee = 1;

    function becomeMember() public returns(bool) {
        require(msg.value == 1, "Did not pay exact fee!");
        isMember[msg.sender] = true;
        return true;
    }

}

contract User {

    function applyForMembership(Club club) public {
        club.becomeMember{value: 1}();
    }

}
```

If you try to compile this code, however, you will arrive at an angry compiler telling you that you cannot send value to a "nonpayable" function. What we are missing is to specify that `becomeMember` can receive ether. To do this, we need to attach the `payable` keyword to the function header of `becomeMember`. The following code includes the `payable` keyword:

```solidity
contract Club {

    mapping(address => bool) isMember;

    uint membershipFee = 1;

    function becomeMember() public payable returns(bool) {
        require(msg.value == 1, "Did not pay exact fee!");
        isMember[msg.sender] = true;
        return true;
    }

}

contract User {

    function applyForMembership(Club club) public {
        club.becomeMember{value: 1}();
    }

}
```

{% hint style="info" %}

#### Note on `payable` and Visibility

Since sending ether inherently changes the state of the blockchain, any function that receives ether *cannot* be marked as `pure` or `view`. Furthermore, any functions related to the sending of ether (i.e. functions that send ether) also cannot be marked as `pure` or `view`.
{% endhint %}

#### Preview: Responding to Ether Received

In the case that we send ether alongside a function call, the logic of the function involved will be triggered. However, what if we send ether to a contract without calling a specific function (i.e. sending ether via the `call` function)? Intuitively, one would guess that nothing happens, just like when we send ether to an EOA. However, as it turns out, contracts can be programmed to respond when they receive ether. Via [*default functions*](/introduction/hello-world-pt.-2/default-functions), developers can dictate what a contract does when it receives ether.


# Modifiers

A New Type of Function

Although we have already covered [functions](/introduction/hello-world/functions), there is one aspect of functions that we have yet to explore.&#x20;

To see the current problem with our understanding of functions, consider the *visibility* of functions; by specifying the visibility of a function, we can restrict actors from accessing a function. However, this becomes an issue if we want to allow *some* actors to call a function, while denying permission to others. With our understanding of visibility, we know that this is not possible. Therefore, are we doomed?

It seems as if we could implement permissioned functions if we could *modify* the logic of our function. And indeed, this is what modifiers allow us to do! Below is the general syntax for modifiers:

```solidity
modifier modifierName(P) {
    // Insert logic here
}
```

where `P` are the parameters that the modifier takes in. To see how modifiers are used, assume we have the following contract:

```solidity
contract Bank {

    address owner;
    
    function withdraw() public {
        // Withdraw logic goes here
    }

}
```

As the name intends, `Bank` holds a large sum of money and `withdraw` is a function is which we *only want the owner* to be able to call. For this guarantee to hold true, lets create a modifier `onlyOwner` that, when assigned to a function, only allows for the owner (i.e. deployer) of a contract to be able to call.

{% code lineNumbers="true" %}

```solidity
modifier onlyOwner() {
    require(msg.sender == owner, "You are not the owner!");
    _;
}
```

{% endcode %}

To understand the logic of `onlyOwner`, it is best to examine the modifier line-by-line:

* Line 2: we are *requiring* that the current message sender is the owner of the contract. If this invariant does not hold, the transaction will fail
* Line 3: the underscore implies that we are reverting control back to the function itself

The following code utilizes `onlyOwner`:

{% code lineNumbers="true" %}

```solidity
contract Bank {

    address owner;
    
    modifier onlyOwner() {
        require(msg.sender == owner, "You are not the owner!");
        _;
    }
    
    function withdraw() public onlyOwner {
        // Withdraw logic goes here
    }

}
```

{% endcode %}

In the function header of `withdraw`, we are calling `onlyOwner`; `withdraw` will execute if the owner of `Bank` is the one calling the function.

Although the `_` symbol inside modifiers might imply that we are reverting control back to the calling function for the rest of the execution context, this is actually not the case! Consider the following code:

{% code lineNumbers="true" %}

```solidity
contract Restroom {

    bool isOccupied;
    
    modifier lock() {
        require(!isOccupied, "Bathroom is already occupied!");
        isOccupied = true;
        _;
        isOccupied = false;
    }
    
    function useRestroom() public lock {
        // Logic goes here
    }

}
```

{% endcode %}

The contract represents a restroom where only one person is allowed at a time. Assuming that *anyone can call `useRestroom` at any time*, the `lock` modifier allows us to adhere to this invariant. Examining the modifier line-by-line:

* Line 6: we are checking if the restroom is occupied; if so, our function will revert
* Line 7: we are "locking" the restroom
* Line 8: we delegate control back to `useRestroom`
* Line 9: after `useRestroom` is finished executing, control is delegated back to `lock`, where it "unlocks" the restroom


# Dynamic Arrays and Strings

Advanced Data Structures

As it will become clear later on, working with constant-size data structures allows us to organize values in an *efficient* matter. However, perhaps the biggest drawback of constant-size data structures is that we cannot adjust the size to meet our demands.&#x20;

Consider a painter that wishes to keep track of the paintings he produces in a list (we assume each painting can be represented by an arbitrary integer).&#x20;

```solidity
contract Painter {

    uint N = 100;
    uint[N] paintings;

}
```

Although we can change `N` to be whatever size we want it to be (at contract creation time), its pretty obvious to see that when the painter paints his `N + 1` painting, there will not be enough space in the `paintings` array to store his painting.

*Dynamic* *types* allow us to create data structures whose size is not fixed. Furthermore, learning about dynamic types will allow us to focus our attention towards strings.


# Dynamic Arrays

The Cooler Version of Static Arrays

Recall in Data Structures that we can declare a static array as follows:

```solidity
T[] arrayName = new T[](n);
```

where `T` is the type of the array. The good news is that defining a dynamic array is not that much different; below is the syntax for declaring a dynamic array:

```
T[] arrayName;
```

Declaring a dynamic array seems simple, but what about defining a dynamic array? Below is the syntax to define a dynamic array:

```solidity
T[] arrayName = [] // Insert array elements here
```

Since *dynamic arrays* can vary in size, we would ideally like to have functions available to us which allow us to grow/shrink our array. And indeed, Solidity provides us with such functions.&#x20;

* `array.push(x)`: pushes `x` to the back of `array`
* `array.pop()`: pops the last element of `array`

And like with static arrays, we can also index into dynamic arrays; it should be noted, however, that this can cause runtime errors if the user is attempting to index into an undefined location of a dynamic array.

{% hint style="info" %}

#### Dynamic Arrays in Memory?

As with mappings, dynamic arrays are reserved only for state variables; one cannot declare/define a new dynamic array variable within a function.
{% endhint %}


# Strings

Everyone's Favorite Type

Having seen dynamic types in action (via dynamic arrays), it is time that we look at the type that has been absent from our discussion of the Solidity programming language: strings. Like most other programming languages, Solidity allows us to write literal text via strings, which have no size restriction.

As with most programming languages, strings in Solidity are an array of characters (represented by a byte); below is the syntax to declaring a string:

```solidity
string M stringName;
```

where `M` is the location of the string; below are the following locations where a string can be defined:

* `memory`: within the body of a function and as a function parameter
* `calldata`: as a function parameter

Below is a code snippet which uses strings extensively:

```solidity
contract StringExample {

    string name = "John";
    
    function getName() public view returns(string memory) {
        return name;
    }
    
    function setName(string calldata _name) public {
        name = _name;
    }

}
```


# Errors

Maintaining Invariants

In an ideal world, we want our programs to contain the logic necessary to handle *all cases*. X occurred? No problem, there is some code to handle X. Y occurred? Not to worry, there is also some code to handle Y. However, we do not live in an ideal world, and below are two reasons as for why this is the case:

* In certain situations, we *cannot* account for *every single case*, especially if the number of cases is of a large magnitude
* In certain situations, we *do not have a good solution* for solving a problem our program is in

Fortunately, we can utilize the concept of **errors** to help navigate the flawed world that we live in. More specifically, we can use errors to respect invariants - properties of our programs (i.e. smart contracts) that we wish to maintain throughout its lifetime.&#x20;

### Raising Errors via `require`

For this section, we refer to `Bank` which is a smart contract designed to act like a bank:

{% code lineNumbers="true" %}

```solidity
contract Bank {

    mapping(address => uint256) balances;

    function deposit() public payable {
        balances[msg.sender] += msg.value;
    }

    function withdraw(uint256 amount) public {
        (bool success, ) = address(msg.sender).call{value: amount}("");
        balances[msg.sender] -= amount;
    }

}
```

{% endcode %}

Assuming that `deposit` is *correct*, we will focus on the `withdraw` function; this function is designed to send the caller the desired amount of ether they wish to withdraw. Ideally, we want for `withdraw` to maintain the following invariants of our `Bank` contract:

* Users cannot withdraw more than what they have in the bank
* The bank successfully sends users their ether

As it currently stands, `withdraw` does not respect the invariants listed above. However, we can utilize errors to revert the execution of `withdraw` if either invariant is not respected. In particular, we will utilize the `require` keyword to maintain both invariants.

Focusing on the first invariant, we want to check that the user is not withdrawing more than what they have in the bank. In terms of pseudocode, we want the following boolean condition to hold:

```
balance of caller >= amount requested to be withdrawn
```

The `require` keyword takes a boolean condition `e`; if `e` evaluates to true, nothing happens. Otherwise, if `e` evaluates to false, the transaction reverts. Therefore, we want to incorporate the following code into our contract:

```solidity
require(balances[msg.sender] >= amount);
```

Furthermore, the `require` keyword also accepts an optional error message. Updating our code to include an error message, we have the following updated contract:

```solidity
contract Bank {

    mapping(address => uint256) balances;

    function deposit() public payable {
        balances[msg.sender] += msg.value;
    }

    function withdraw(uint256 amount) public {
        require(balances[msg.sender] >= amount, "Not enough funds!");
        (bool success, ) = address(msg.sender).call{value: amount}("");
        balances[msg.sender] -= amount;
    }

}
```

At this point, `withdraw` now respects the first invariant; but what about the second invariant? Recall from the [contract interactions](/introduction/hello-world-pt.-2/contract-interactions) section that the `success` variable in `withdraw` indicates whether if the payment of ether was successful. Therefore, we just need to check that `success` is equal to true. Updating our `Bank` smart contract one final time, we have:

```solidity
contract Bank {

    mapping(address => uint256) balances;

    function deposit() public payable {
        balances[msg.sender] += msg.value;
    }

    function withdraw(uint256 amount) public {
        require(balances[msg.sender] >= amount, "Not enough funds!");
        (bool success, ) = address(msg.sender).call{value: amount}("");
        require(success, "Sending ether failed");
        balances[msg.sender] -= amount;
    }

}
```

### `revert` and `assert`

In addition to `require`, we also have the keywords `revert` and `assert` which also allow us to raise errors in our smart contracts.

`revert` acts very similarly to `require`, except that no boolean condition is needed for `revert`. An example of this can be seen below (the error message is optional, but recommended):

```solidity
function failingFunction() public {
    revert("Failing just cause");
}
```

The last keyword we will discuss is `assert`. `assert` only takes in a boolean condition. However, what makes `assert` special is that if it is ever called by a smart contract, all the gas remaining in said transaction is utilized! Whereas for `require` and `revert` refund any remaining gas.


# Events

A Special Kind of On-Chain Data

Up until this point of the textbook, all on-chain data has revolved around the smart contract; that is, any information that we associated as living on the blockchain was always tied to an associated contract (i.e. via a state variable). In this section, we will go over *events*, a different form of on-chain data that allows us to permanently store data without having it be stored within a smart contract.

### Motivation: Dummy ERC-20 Token

To understand events, we will introduce the example of a "dummy" ERC-20 token called... `Dummy`.

{% code title="Dummy.sol" %}

```solidity
contract Dummy {

    function transfer() public {}

}
```

{% endcode %}

Like all ERC-20 tokens, `Dummy` has the logic necessary to transfer tokens from one address to another, via the `transfer` function. However, rather than being able to just transfer tokens between addresses, we would also like to *keep track of every transfer*. Whether you are the IRS or your average on-chain data scrapper, there is good reason to track *transfer events*.&#x20;

To start, one might track transfer events via the following principle:

> Create a state variable which stores all transfer events (as a list). Furthermore, for each transfer event, append the event to the transfer event list

An updated implementation of the `Dummy` token with this new principle in mind might look like as follows:

```solidity
contract Dummy {

    struct TransferEvent {
        address from;
        address to;
        uint amount;
    }

    TransferEvent[] allTransfers;

    function transfer() public {
        address from = msg.sender; // Arbitrary values
        address to = address(0x0);
        uint amount = 1;
        // Transfer logic goes here
        allTransfers.push(TransferEvent(from, to, amount));
    }

}
```

Examining each segment of this new `Dummy` contract in depth:

* `TransferEvent`: struct which represents a transfer event. This struct holds the from address, to address, and value transfered.
* `allTransfers`: a dynamic array which holds all transfer events
* `transfer`:  function which executes a transfer. At the end of this function, the transfer event is appended to `allTransfers`.

Our new implementation of the `Dummy` token is now able to keep track of every transfer event! However, the biggest issue with this is that this new implementation is ***egregiously inefficient***. Storing all transfer events into a state variable is extremely costly when considering the `transfer` function might be called thousands of times per day. Are we doomed?

### Events

As the last section made it evident, we want a way to store transfer events without having to use any contract data. For this, we can leverage *events*. The syntax for events is as follows:

```solidity
event eventName();
```

Inside the parentheses is where we can list the arguments of our event (i.e. the data that we want logged). In the case of our transfer event, we can define the following:

```solidity
event Transfer(address from, address to, uint value);
```

Finally, now that we have our event defined, we can *emit* our event as follows:

```solidity
emit Transfer(from, to, value);
```

Tying all these ideas, the following code is an updated version of our `Dummy` token which utilizes events to record transfers:

```solidity
contract Dummy {

    event Transfer(address from, address to, uint value);
    
    function transfer() public {
        address from = msg.sender;
        address to = address(0x0);
        uint value = 1;
        emit Transfer(from, to, value);
    }

}
```

Many might be wondering where events are actually stored? As mentioned previously, events are not stored as state variables; rather, events are stored as part of the transaction receipt. By storing data in this format, users are able to store on-chain data without having to utilize state variables.

{% hint style="info" %}

#### Who Can Access Event Data?

Because events are stored as part of the transaction receipt, all nodes (and users with access to a node) can access events. However, this also means that contracts cannot access event data.
{% endhint %}

### Indexing

Although we showed how to store data in events, we have not shown how to prioritize certain types of data within an event. To understand why we might want to prioritize certain data fields in our events, consider again the `Transfer` event:

```solidity
event Transfer(address from, address to, uint value);
```

Although we will get more into how events are represented when we discuss the EVM, the general structure of events is as follows:

* Event ID
* Event Data

With our current implementation of the `Transfer` event, our event data field is as follows (utilizing arbitrary values for the from, to, and value fields):

<pre><code><strong>0x000000000000000000000000Fb2b43852f444DE82643ec58BDc125Ee4EBeDad6000000000000000000000000549889FA3522717E7F1665647BA89f58DE8D277d0000000000000000000000000000000000000000000000000000000000000001
</strong></code></pre>

Although it is possible to extract the arguments from event data field, most can agree that the data field, as it stands, is a mess. Ideally, we want a way to structure our event data so that we can access the arguments of an event directly without having to do any sort of decoding; that is, we want to *prioritize* certain arguments. For this, we can mark the arguments we want to prioritize with the `indexed` label; the following code is an updated of our `Transfer` event that utilizes indexed arguments:

```solidity
event Transfer(address indexed from, address indexed to, uint value);
```

Using indexed arguments, the new structure of our event is as follows:

* Event ID
* `from` Argument: `0x000000000000000000000000Fb2b43852f444DE82643ec58BDc125Ee4EBeDad6`
* `to` Argument: `0x000000000000000000000000549889FA3522717E7F1665647BA89f58DE8D277d`
* Event Data: `0x0000000000000000000000000000000000000000000000000000000000000001`

Using indexed arguments, we can now clearly access the arguments of our event. *Note that the structure of an event is dependent on the ordering of the event arguments.*

{% hint style="info" %}

#### Index Everything?

Although it seems that indexing all arguments is ideal, consider the following:

* We can only index at most 3 arguments in an event
* The amount of gas used in emitting an event is dependent on the number of arguments indexed. Therefore, in the case of our `transfer` event, although we could have indexed all three arguments, it is better from a gas perspective to index only two arguments since *we know* that the data field contains *just* the value transferred.
  {% endhint %}

### Resources

Events in Go Ethereum: <https://goethereumbook.org/events/>


# Units and Global Variables

Working with numbers, made easy!

Solidity provides a wide array of predefined units and variables that facilitate both logical and mathematical calculations. In this section we will go over some of the most commonly used ones.

### Units

Solidity provides two types of units: *ether units* and *time units*.&#x20;

#### Ether Units

Recall that the EVM denominates ether in terms of Wei (1 wei  = 10\*\*(-18) ether). Therefore, consider the following code that checks if an account has a balance of 1 ether:

```solidity
if (account.balance == 10**18) {}
```

Having to specify an exact integer, especially for integers as large as `10**18`, can lead to both bugs and legibility issues. Rather than specifying an exact value, we can utilize units.

```solidity
if (account.balance == 1 ether) {}
```

In the code above, we are checking if the account has exactly 1 ether. Under the hood, we are still checking if the account has a balance of `10**18`, but it is obvious that using units makes our code easier to read and less prone to bugs. Likewise, we can use units in state/local variables as well:

```solidity
uint num = 1 ether;
```

Solidity provides us with the following ether units:

* `wei`
* `gwei`
* `ether`

#### Time Units

The EVM (and computers in general) denominate time in terms of seconds. Dealing with seconds might be a headache for some and for this reason, Solidity provides us with *time units*. As an example, consider the following code:

```solidity
function redeem() public {
        if (lastTimeCalled + 3600 < currentTime) {}
}
```

In the code above, we have a function which performs some logic if it has been more than a hour since the last time it was called; we check this by adding `3600` to `lastTimeCalled`. Again, denominating seconds explicitly introduces both legibility issues and the potential for bugs. Instead, we can use time units:

```solidity
function redeem() public {
        if (lastTimeCalled + 1 hours < currentTime) {}
}
```

Listed below are the time units that Solidity provides:

* `seconds`
* `minutes`
* `hours`
* `days`
* `weeks`

Again, note that all these units are denominated in terms of seconds.

### Global Variables

As the name might suggest, *global variables* are values that are available at a global level. Listed below are some of the most commonly-used global variables:

* `block.gaslimit`: the gas limit of the current block
* `block.number`: the number of the current block
* `block.timestamp`: the time at which the current block was published
* `msg.data`: the data attached with the current message
* `msg.sender`: the sender of the current message
* `msg.value`: the amount of ether (denominated in wei) that was sent with the current message
* `tx.gasprice`: the price per gas paid for the current transaction
* `tx.origin`: the account that sent the current transaction

#### Transactions Vs Messages

Although transactions and messages might seem synonymous, there is a big difference between the two terms. Consider the following transaction:

> I call contract A. Halfway through the execution of contract A, contract A calls contract B.

Transaction here refers to the entire execution context. In this scenario, `tx.origin` is my address (`tx.origin` is always the address of an EOA). Messages, meanwhile, refer to a specific execution environment. In the scenario above, there are two messages:

* Message 1: consists of me as the sender and contract A as the recipient.
* Message 2: consists of contract A as the sender and contract B as the recipient.

An easy way to think about messages is that a new message is created whenever the from/to values of the execution environment changes.&#x20;

### Resources

Solidity Docs: <https://docs.soliditylang.org/en/v0.8.17/units-and-global-variables.html>


# Default Functions

Calling Contracts w/o Calling Any Functions

In Contract Interactions, we were introduced to the idea of calling other smart contracts and in particular, the `call` function. Recall if we just want to send ether to an account, we can use the following code:

```solidity
account.call{value: 1}("");
```

When talking about the `call` function, we examined it from the perspective of the sender, but not from the perspective of the recipient. And as it turns out, there is *lots to talk about* if the recipient of a `call` function is a smart contract. In this section, we focus on *default functions* - functions that are automatically run whenever a target function is not specified.

### How Default Functions are Activated

Although we will not get into the specifics of how the calldata of the call function is organized, note that if we are calling a specific function, said function's id is contained within the calldata. Therefore, it is evident that if we pass in the empty string as the calldata for the call function, we are not specifying a specific function to call.&#x20;

### `receive` and `fallback`

In the case that we do not specify a function to call (and we are calling a smart contract), the following two scenarios can occur:

* The transaction reverts (occurs when a default function is not implemented)
* Execution is handled by a default function

What is a default function, you might ask? The following is the syntax for the two types of default functions that can be implemented in a smart contract:

```solidity
receieve() external payable {}
fallback() external {}
```

The first thing to notice about default functions is that they lack the `function` keyword; default functions are a special type of function and therefore, they are declared via the `receive` and `fallback` keywords.&#x20;

Now that we are aware of the two types of fallback functions, the next logical question to ask is how are the two functions different? The difference between `receive` and `fallback` lies in when they are called. Assume we are sending ether to a contract; the following logical chart explains what function is called (credit goes to [Solidity-by-Example](https://solidity-by-example.org/) for this):

* If we are passing in the empty string as calldata
  * `receive` is called *if it exists*
  * `fallback` is called *only if it is marked as payable*
* If we are **not** passing in the empty string as calldata
  * `fallback` is called *if it exists*

As for what default functions can do, recall that default functions are... functions and so anything that functions can do, default functions can do as well.

### The Security Nightmare of Default Functions

Although default functions appear mundane, their existence is the cause for one of the most common security vulnerabilities for smart contracts - the reentrancy attack. We will investigate the reentrancy attack in-depth when discussing smart contract security, but for now, we will discuss reentrancy attacks from a high level.

Assume we have a smart contract `Bank` which acts like a bank. Users can deposit and withdraw ether as they wish via the `deposit` and `withdraw` functions, respectively. Furthermore, the amount of ether a user has in the bank is tracked via a mapping variable.

{% code title="Bank.sol" %}

```solidity
contract Bank {

    mapping(address => uint256) balances;

    function deposit() public payable {
        balances[msg.sender] += msg.value;
    }

    function withdraw(uint256 amount) public {
        require(balances[msg.sender] >= amount, "Not enough funds!");
        (bool success, ) = address(msg.sender).call{value: amount}("");
        require(success, "Sending ether failed");
        balances[msg.sender] -= amount;
    }

}
```

{% endcode %}

Now consider Eve, a malicious smart contract who wants to drain the bank of all its funds. Eve cannot change the logic of the smart contract, but she can dictate her own logic. Furthermore, Eve notices the following logical path of the `withdraw` function:

* The bank first checks if the user has enough funds to withdraw the desired amount
  * If not, the transaction reverts
* The bank sends the caller their desired funds
* The bank checks if the funds were able to be sent
  * If not, the transaction reverts
* The bank updates the balance of the caller to account for the withdraw

The most important thing to note here is that *the balance of the caller is updated **after** the funds are sent*. And once again, because Eve can dictate her own logic, she can dictate the logic of her `fallback` function. Therefore, if Eve is able to repeatedly call `withdraw` prior to her balance being updated by the bank, she is able to drain the bank of all its funds. This, in essence, is the reentrancy attack - looping through the execution of a function multiple times through default functions.&#x20;

{% code title="Eve.sol" %}

```solidity
contract Eve {

    Bank bank;

    function attack() public {
        bank.withdraw(1 ether);
    }

    fallback() external payable {
        if (address(bank).balance >= 1 ether) {
            bank.withdraw(1 ether);
        }
    }

}
```

{% endcode %}

Above is the logic that Eve would implement to conduct the reentrancy attack. In particular, there are two functions to consider here:

* `attack`: starts the reentrancy attack by calling `withdraw`
* `fallback`: executed whenever the bank sends the funds to Eve. It first checks if the bank has enough funds to be siphoned. If so, the function calls `withdraw`

The logical path of the entire reentrancy attack is described below:

* `attack` is called, which starts the reentrancy attack by calling `withdraw`
* `withdraw` executes until it calls Eve
* `fallback` is executed, and calls `withdraw`
* `withdraw` executes until it calls Eve
* `fallback` is executed, and calls `withdraw`
* ...
* `fallback` is executed; Eve sees that the bank no longer has enough funds to be siphoned and ends the reentrancy attack
* `withdraw` finishes the rest of its execution

### Resources

<https://solidity-by-example.org/sending-ether/>


# Node Providers


# Interacting With On-Chain Contracts

Building => Using Smart Contracts

In the first of two asides, we will discuss how to *utilize* smart contracts. At this point of the textbook, we have become proficient Solidity developer and have become accustomed to *building* smart contracts. While it is important to learn the art of building smart contracts, an equally important skill is learning how to interact with them outside the Solidity programming language.&#x20;

Given that the computer science curriculum at Cornell gives students a substantial amount of experience in Python, we will approach this section from a data science perspective.&#x20;

### Prerequisites

For this section, we required that students have both Python and Jupyter Notebooks installed on their computers.

### What Library to Use?

The first question that comes to mind is what library to use to interact with contracts? Each library has its pros and cons, but we can separate libraries by programming languages:

* JavaScript: web3.js, ethers.js
* Rust: ethers.rs
* Python: web3.py

For the purposes of this section, we will be using web3.py

### Getting Started with web3.py

Assuming that you have Python and Jupyter Notebook installed on your computer, the first step in getting started with web3.py is to download the library itself.

```bash
pip install web3
```

Afterwards, create a new Jupyter Notebook and create a new cell with the following code:

{% code lineNumbers="true" %}

```python
from web3 import Web3
w3 = Web3(Web3.HTTPProvider(api_url))

print(w3.isConnected())
```

{% endcode %}

where `api_url` is the API URL of the node that you wish to connect you. Services such as <https://www.quicknode.com/> and <https://www.alchemy.com/> allow you to sign up for a free plan and gain access to a node from which you can use to interact with blockchains like Ethereum.

If all goes well, the following should be printed when you run the cell above:

```
True
```

Awesome, you are now able to connect to the blockchain (via a node provider)! But what does the code you just ran actually mean? Inspecting the code on a line-by-line basis:

* Line 1: we are importing the web3 libary into our Python script
* Line 2: we are creating a web3 object (`w3`). By using a web3 object, we are able to call it to perform a wide array of blockchain-related actions
  * As part of its constructor, we can pass in an `HTTPProvider` (i.e. the API URL). This allows our web3 object to have access to the blockchain.
* Line 4: we are checking if our web3 object has access to the blockchain

Although the web3 class is useful when interacting with the blockchain, it also provides us with many "offline" functions that make our lives easier. An example of this is the `keccak` function; while one can compute the hash of a string manually, this would be egregiously tedious. However, the web3 class provides a built-in `keccak` function that does this for us:

```python
message = "Solidity is Awesome!"
print(w3.keccak(text=message).hex())
```

If you run the code block above, you will produce the following hash:

```
0x7afee51dff6870d56cf757e9eb2d8fd31a91153fc307867f43f06990c1b349c6
```

### Querying the Blockchain

For the rest of this section, we will focus on the "online" functionality that the web3 class provides us. To start, we will need to narrow our scope from the web3 object to the web3.eth object; this object provides us with the functionality to read from and write to the Ethereum blockchain. To start. Although there are a countless number of things we can do to interact with the blockchain, perhaps a good starting point is to get the current block number:

```python
print(w3.eth.get_block_number())
```

At the time of writing this, the following was printed when running the cell above:

```
17559222
```

Taking things one step further, what if we want to get information about a block and its contents? Below is the code snippet for how we might want to do that:

```python
block_info = w3.eth.get_block('latest')

# block_info contains a lot of data, so we're just going to print the most relevant data, full details can be found at https://web3py.readthedocs.io/en/v5/web3.eth.html

print(f"block number is {block_info['number']}")
print(f"block difficulty is {block_info['difficulty']}")
print(f"block miner is {block_info['miner']}")
print(f"total number of transactions is {len(block_info['transactions'])}")
```

At the time of writing this, the following was printed when running the cell above:

```
block number is 17559240
block difficulty is 0
block miner is 0xDAFEA492D9c6733ae3d56b7Ed1ADB60692c98Bc5
total number of transactions is 110
```

As another example of how we can leverage the web3.eth object to query the blockchain, the following code demonstrates how we can get the balances of accounts:

```python
# Getting my account balance
my_bal = w3.eth.get_balance("0x7f610402ccc4CC1BEbcE9699819200f5f28ED6e3")
print(f"my balance is {w3.fromWei(my_bal, 'ether')} ether")

# Getting balance of WETH smart contract
contract_bal = w3.eth.get_balance("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2")
print(f"the WETH contract balance is {w3.fromWei(contract_bal, 'ether')} ether")
```

```
my balance is 0.004094900992929802 ether
the WETH contract balance is 3984615.278144831435284183 ether
```

### Resources

* web3.py Documentation: <https://web3py.readthedocs.io/en/stable/index.html>


# Migrating to Foundry & VS Code

Your Best Friends When Developing

When learning how to write Solidity, we used **Remix**, the preferred editor of choice for beginners. Using Remix, we've become familiar with writing and deploying our smart contracts. And while it is possible to utilize Remix for what is to follow in our textbook, we believe that now is the appropriate time to shift from using Remix to tools that are more popular among the smart contract developer community.&#x20;

But what tool will we be using now? Ideally, we want to use a tool that allows us to compile our contracts with ease, allows for a wide range of testing options, and provides a wide array of functionality that makes our lives as smart contract developers easier. For most, the following two frameworks come to mind:

* HardHat
* Foundry

Both frameworks are widely used and are widely respected. For the purposes of this course, however, we will be using Foundry. Although most smart contract development courses focus on teaching student HardHat, we've chosen Foundry for the following reasons:

* **No Additional Languages**: With regards to testing/scripts, HardHat uses JavaScript/TypeScript. Therefore, developers who wish to use HardHat are forced to juggle with both Solidity and JS/TS. Foundry, meanwhile, allows for tests and scripts to be written *entirely* in Solidity.&#x20;
  * It should be noted that at Cornell University, the Computer Science curriculum is geared towards teaching students Python and Java (via CS1110 and CS2110). As a result, the majority of the students who enroll in CS4998 do not have any experience programming in JavaScript. Foundry, therefore, facilitates the process of learning to write smart contracts by not having to learn JavaScript
* **Projected Usage**: Foundry benefits from the fact that it is funded and built by [Paradigm](https://www.paradigm.xyz/), one of the premier VC funds in the blockchain industry. Furthermore, since its release, Foundry has seen mass adoption and receives updates regularly. We are of the belief that Foundry will continue to be grow in utilization and for this reason, we choose to use Foundry.

While we are able to install Foundry by itself, we can't do much with it without having some sort of program that allows us to write our Solidity code. For this, we will rely on Visual Studio Code, an IDE that will provide us with an interface to write our Solidity code.&#x20;

{% hint style="info" %}

#### Is VS Code Necessary?

No! As a Cornell professor once said:\
\
*I'm too old for this VS Code stuff*\
\
Since Foundry works independently of VS Code, we can also use text editors such as Vim to write our Solidity code. After all, we will be using the command line to compile our contracts and perform other actions related to smart contract development.
{% endhint %}

### Installing VS Code

For this section, please refer to <https://code.visualstudio.com/download> for directions on how to install Visual Studio Code on your local device.&#x20;

#### Installing the Solidity Extension

Now that we have VS Code installed on our device, we *could* get right away with writing Solidity locally. However, as it will become apparent, writing Solidity using vanilla VS Code will seem as if we are using a basic text editor.

As an example, consider the following program that we wish to write in VS Code:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Dummy {

    function getOne() public pure returns(uint) {
        return 1;
    }

}
```

If we were to write this code in VS Code, we would get the following:

<figure><img src="/files/cD1CCDSTAXwYn0IRgPB7" alt=""><figcaption><p>Solidity Code in Vanilla VS Code</p></figcaption></figure>

The main thing missing from VS Code is the lack of syntax highlighting and other key langauge features that Remix provided for us. To supplement this, we will install the Solidity extension - this extension will provide syntax highlighting and the other language features that we have been accustomed to.

If you search "Solidity" in the extensions tab of Solidity and look at the extensions offered, you will come across the following:

<figure><img src="/files/1xymAUwNXBSOcSvC2uT5" alt=""><figcaption><p>Solidity Extension</p></figcaption></figure>

This is the extension we will be using; after installing it, your Solidity code should now look like this:

<figure><img src="/files/j4ZMsJGtskjveTqsIRuh" alt=""><figcaption><p>Solidity w/ Extension</p></figcaption></figure>

### Installing Foundry

The next step in this section is to actually install Foundry itself. To do this, go to <https://getfoundry.sh/> and run the command provided to you in your terminal.

{% hint style="info" %}

#### Not Able To Install Foundry?

If connect to Cornell WiFi, you will be unable to download Foundry due to .xyz domains being blocked by the university. Please use a external network to download Foundry.
{% endhint %}

### Structure of a Foundry Project

Now that we have Foundry installed, we will briefly go over the three main tools that Foundry offers:

* **Forge**: this is the framework we will be using when developing with our contracts (i.e. compiling, testing, etc.)
* **Cast**: provides a wide range of functionality that allows us to interact with the blockchain (i.e. via read/write commands)
* **Anvil**: allows us to spin up our own local Ethereum node, which will prove to be very useful when testing

In the sections to come, we will learn the basics of using these three frameworks.&#x20;

### Resources

* Foundry GitHub Repository: <https://github.com/foundry-rs/foundry>
* Foundry Book: <https://book.getfoundry.sh/>


# The Basics of Forge


# Installing and Using Dependencies


# Cast


# Anvil


# The Ethereum Virtual Machine

Simple, but Powerful

> The concept of an arbitrary state transition function as implemented by the Ethereum protocol provides for a platform with unique potential; rather than being a closed-ended, single-purpose protocol intended for a specific array of applications in data storage, gambling or finance, Ethereum is open-ended by design, and we believe that it is extremely well-suited to serving as a foundational layer for a very large number of both financial and non-financial protocols in the years to come\
> \
> \- Vitalik Buterin, *The Ethereum Whitepaper*

At this point of the course, you have become very familiar with the Solidity syntax and are able quite complex smart contracts. While it might appear that there is little left to teach regarding Solidity, the opposite is true; we have learned most of what smart contracts are capable of from a high level, but there is an entire world that we have yet to explore.&#x20;

Therefore, it is appropriate that we cover the machine that runs your smart contract code: the **Ethereum Virtual Machine (EVM)**. Understanding the EVM serves as a foundation for understanding the security and gas optimization practices that *advanced* smart contract development requires. Furthermore, learning about the EVM will allow us to introduce **Yul**, a low-level programming language used within Solidity that allows for low-level programming (in contrast to Solidity's high-level programming).

It is important to note that the EVM is an *architecture*. There does not exist an EVM; rather, there exists multiple implementations of the EVM (i.e. execution clients) which implement the necessary functionality for the client to compute data from the blockchain. Outside of this though, each execution client has the freedom to implement their version of the EVM as they wish (an obvious example of this is the language which an execution client is written in).

Learning about the EVM may seem scary at first; for many, this will be the first time learning about how a computer works (albeit, a *very simple computer*). Therefore, we will begin our journey of learning the EVM by first examining an elementary computer before discussing the **Turing Machine**, the most basic type of computer in which all computers can be reduced to. Afterwards, we will introduce the EVM by discussing the operations the EVM is capable of performing and later introducing the other components that define the EVM.


# A First Look at Computers

Examining an Elementary Computer

Before we examine the EVM, we will look at a very simple computer to get an understanding of the process a computer undergoes when running. By understanding the logistics of an elementary computer, we will have the foundation necessary to understand more sophisticated computers.

### The Cuzco Model

The Cuzco Model is a basic computer that I have created for the purposes of this section; this computer is capable of performing basic arithmetic operations.&#x20;

To get started, we will first discuss the *architecture* of the Cuzco Model - i.e. the components that the Cuzco Model is comprised of:

* Instructions: an ordered list of operations that the computer will perform. This list is 0-indexed.
* Program Counter (PC): keeps track of the current instruction to execute
* Stack: data structure that is updated via operations

Now that we have discussed the architecture of the Cuzco Model, we will examine the stack - although simple, the stack allows us for the Cuzco Model to be memoryful. Otherwise, we can perform an infinite number of operations and our computer would still remain in the same "state". Like all stacks, the stack that the Cuzco Model utilizes operates on a last-in, first-out principle. However, this stack grows up, not down.

Next, we discuss the operations that the Cuzco Model is capable of; listed below are a&#x20;

| Operation   | Operation Code (OpCode) | Description                                                                                                                   |
| ----------- | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| Push **v**  | **0x00 v**              | Pushes **v** to the top of the stack                                                                                          |
| Addition    | **0x01**                | Pops the top two elements from the stack, computes their sum, and pushes the result to the stack                              |
| Subtraction | **0x02**                | Pops the top two elements from the stack, computes their difference, and pushes the result to the stack                       |
| Logical And | **0x03**                | Pops the topmost element from the stack, computes the logical and operation of the value, and pushes the result to the stack  |
| Bitwise And | **0x04**                | Pops the topmost elements from the stack, computes the bitwise and operation of the value, and pushes the result to the stack |
| Stop        | **0x05**                | Stops execution of the program                                                                                                |

Finally, listed below are the invariants that the Cuzco model abides by:

* All values on the stack are 8-bit signed integers.&#x20;
  * Furthermore, there is no protection against integer overflow
* The stack has a maximum depth of 4 values

Now that we have the specification of the Cuzco Model outlined, let's examine a couple of examples that show the power of the Cuzco Model!

### Example One: Adding Two Values

We begin by adding two numbers; although this may seem like an easy task at first, there are multiple steps involved for the Cuzco Model to execute this.&#x20;

Assume that we want to compute the sum of 1 + 2. The first step in this is to get the values 1 and 2 onto the computer itself. To do this, we can use the Push operation; listed below are the two Push operations that we need to input 1 and 2 into the Cuzco Model:

```
0x00 0x01 (Push 2)
0x00 0x02 (Push 1)
```

Now that we have the two values 1 and 2 pushed onto the stack, we want to compute their sum. To do this, we can use the addition operation. Our updated list of instructions is as follows:

```
0x00 0x1 (Push 2)
0x00 0x2 (Push 1)
0x01 (Addition)
```

Finally, to signal to the computer that we are finished executing, we include the Stop operation at the end of our instructions. Therefore, our final set of instructions is as follows:

```
0x00 0x1 (Push 2)
0x00 0x2 (Push 1)
0x01 (Addition)
0x05 (Stop)
```

We now run the Cuzco Model on the set of instructions provided above. Below is a table which shows the state of the computer at each step of execution:

| PC  | Instruction | Stack     | Description                         |
| --- | ----------- | --------- | ----------------------------------- |
| 0x0 | 0x00 0x1    | 1 \|      | We are pushing 1 onto the stack     |
| 0x1 | 0x00 0x2    | 2 \| 1 \| | We are pushing two onto the stack   |
| 0x2 | 0x01        | 3 \|      | We are computing the sum of 1 and 2 |
| 0x3 | 0x05        | 3 \|      | We are terminating the program      |

### Example Two: Multiple Arithmetic Operations

In this next example, we will introduce the subtraction operation. Here, we will compute (1 + 2) - 5, which should yield us the value -2. Rather than having to rewrite our original list of instructions, we just need to add the following two operations:

* We need to push 5 onto the stack before we push 1 and 2
* After computing the sum of 1 and 2, we need to compute the difference of the result and 5

Therefore, our updated set of instructions is as follows:

<pre><code><strong>0x00 0x5 (Push 5)
</strong><strong>0x00 0x1 (Push 2)
</strong>0x00 0x2 (Push 1)
0x01 (Addition)
0x02 (Subtraction)
0x05 (Stop)
</code></pre>

Running the Cuzco Model on the new set of instructions, our state table is as follows:

| PC  | Instruction | Stack       | Description                                |
| --- | ----------- | ----------- | ------------------------------------------ |
| 0x0 | 0x00 0x5    | 5 \|        | We are pushing 5 onto the stack            |
| 0x1 | 0x00 0x1    | 1 \| 5      | We are pushing 1 onto the stack            |
| 0x2 | 0x00 0x2    | 2 \| 1 \| 5 | We are pushing 2 onto the stack            |
| 0x3 | 0x01        | 3 \| 5      | We are computing the sum of 1 and 2        |
| 0x4 | 0x02        | -2 \|       | We are computing the difference of 3 and 5 |
| 0x5 | 0x05        | -2 \|       | We are terminating the program             |

### Example Three: Introducing Logical Operators

In this example, we want to compute the result of 1 && 2 (where && is the logical and operator). Since 1, 2 are nonzero values, this should equal 1. First writing our set of instructions:

```
0x00 0x1 (Push 1)
0x00 0x2 (Push 2)
0x03 (Logical And)
0x05 (Stop)
```

Running the Cuzco Model on the set of instructions above:

| PC  | Instruction | Stack     | Description                                                                                                       |
| --- | ----------- | --------- | ----------------------------------------------------------------------------------------------------------------- |
| 0x0 | 0x00 0x1    | 1 \|      | We are pushing 1 onto the stack                                                                                   |
| 0x1 | 0x00 0x2    | 2 \| 1 \| | We are pushing 2 onto the stack                                                                                   |
| 0x2 | 0x03        | 1 \|      | We are popping 1 and 2 from the stack, computing the logical and operation, and pushing the result onto the stack |
| 0x3 | 0x05        | 1 \|      | We are terminating the program                                                                                    |

### Example Four: Introducing Bitwise Operators

In our final example, we will introduce the usage of the bitwise and operator.

For this example, we want to compute 1 & -2 (where & is the bitwise and operator). Before writing the instructions, we want to first understand what the expected result should be. For this, we will need to represent 1 and -2 in binary notation (using two's complement. Therefore, we have:

```
1 => 00000001
-2 => 11111110
```

The bitwise and operator, as the name might suggest, applies the and operation on a bit-by-bit basis. Therefore, it is easy to see that 1 & -2 = 0. Now that we know what the expected result should be, we write the instructions our compute will execute:

```
0x00 0x01 (Push 1)
0x00 0xFE (Push -2)
0x04 (Bitwise And)
0x05 (Stop)
```

Our state table is as follows:

| PC  | Instruction | Stack      | Description                                                                                                       |
| --- | ----------- | ---------- | ----------------------------------------------------------------------------------------------------------------- |
| 0x0 | 0x00 0x1    | 1 \|       | We are pushing 1 onto the stack                                                                                   |
| 0x1 | 0x00 0xFe   | -2 \| 1 \| | We are pushing -2 onto the stack                                                                                  |
| 0x2 | 0x04        | 0 \|       | We are popping 1 and 2 from the stack, computing the bitwise and operation, and pushing the result onto the stack |
| 0x3 | 0x05        | 0 \|       | We are terminating the program                                                                                    |

### Cuzco Machine, in Solidity!

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract CuzcoMachine {

    int8[] stack;

    function execute(bytes calldata insn) public returns (int8) {
        uint counter = 0;
        while (counter < insn.length) {
            if (insn[counter] == 0x0) {
                // We have push operation
                // Extract pushvalue
                int8 pushValue = int8(uint8(insn[counter + 1]));
                pushOntoStack(pushValue);
                counter += 2;
            } else {
                // Not push operation
                if (insn[counter] == hex"01") {
                    // Add
                    add();
                } else if (insn[counter] == hex"02") {
                    // Subtract
                    subtract();
                } else if (insn[counter] == hex"03") {
                    // Logical And
                    logicalAnd();
                } else if (insn[counter] == hex"04") {
                    // Bitwise And
                    bitwiseAnd();
                } else {
                    // Stop
                    break;
                }
                counter += 1;
            }
        }
        return stack[stack.length - 1];
    }

    function popTwo() private returns (int8, int8) {
        // Pop two values off stack
        int8 a = stack[stack.length - 1];
        stack.pop();
        int8 b = stack[stack.length - 1];
        stack.pop();
        return (a, b);
    }

    function add() private {
        (int8 a, int8 b) = popTwo();
        stack.push(a + b);
    }

    function subtract() private {
        (int8 a, int8 b) = popTwo();
        stack.push(a - b);
    }

    function logicalAnd() private {
        (int8 a, int8 b) = popTwo();
        // We need to convert a, b to boolean values
        bool a_bool = a != 0;
        bool b_bool = b != 0;
        bool result = a_bool && b_bool;
        int8 result_int;
        if (result) {
            result_int = 1;
        }

        stack.push(result_int);
    }

    function bitwiseAnd() private {
        (int8 a, int8 b) = popTwo();
        stack.push(a & b);
    }

    function pushOntoStack(int8 val) private {
        stack.push(val);
        if (stack.length > 4) {
            revert("Stack overflow!");
        }
    }

}
```


# The Turing Machine

What Unites All Modern Computers

The next model of computation that we will examine is the **Turing Machine**. Conceived in the 1930s by British computer scientist Alan Turing, the Turing Machine defines what it means for a problem to be *computable*; to be more specific, many in the computer science field agree that a problem is *computable* if it can be run on a Turing Machine.

We will discuss the architecture of the Turing Machine along with some examples, but perhaps the biggest question on your mind is *why study the Turing Machine*? After all, we are studying a model of computation that is more than 85 years old, and it is difficult to see what relation smart contract development has to Turing Machines.

It turns out, however, that *there is* a connection between the Turing Machine and smart contract development on EVM-chains. That is, **the Ethereum Virtual Machine is Turing Complete**. The same computable problems that can be written for Turing machines can be translated to EVM code, and vice versa!

{% hint style="info" %}

#### A Quick Note on Gas

Many might be quick to point out that the EVM is not Turing Complete due to the fact that any computation is limited by gas. While this is true, we are ignoring the limitations imposed by gas.&#x20;
{% endhint %}

### Tape, Scan, Write, Move

To start, we begin by answering what is perhaps the biggest question on your mind: what does a Turing Machine look like? Below is an image of a Turing Machine:

<figure><img src="/files/LEDaXhC92E4Jb0eQktKX" alt=""><figcaption><p><a href="https://upload.wikimedia.org/wikipedia/commons/0/03/Turing_Machine_Model_Davey_2012.jpg">Turing Machine</a></p></figcaption></figure>

From a physical perspective, there are two main components to a Turing Machine: the tape and the scanner. The tape acts as both the input to the machine and the memory that the machine can utilize throughout its execution. The scanner, meanwhile, is responsible for shifting the position of the tape while reading/writing to it.

Given its tape, each step of execution of the Turing Machine can be defined as follows:

1. **Scan**: the scanner of the Turing Machine reads the current symbol that is printed on the cell the scanner is above
2. **Decide**: given the state of the Turing Machine and the symbol it just read, the scanner then decides *in a deterministic manner* the action it will execute; said action consists of three parts:
   * The symbol to write onto the current tape cell
   * The state to transition to
   * The direction in which to shift the scanner (left or right)
3. **Execute**: with the Turing Machine knowing what to do now, the Turing Machine does the following:
   * Updates its state
   * Write to the tape
   * Move either left or right

Now that we understand the Turing Machine from a high level, we will examine its formal specification alongside some programs that make use of the Turing Machine.

### The Architecture of the Turing Machine

Unlike the Cusco Model, which was defined in a programmatic manner, the Turing Machine is precise in its mathematical specification. A Turing Machine can be characterized by the following six elements:

* $$Q$$ : the set of states the Turing Machine can be in&#x20;
* $$\Sigma$$ : the input alphabet
* $$\Gamma$$ : the working alphabet
* \_ : the blank symbol
* $$F$$ : the set of final states
* $$\delta$$ : the state transition function

We discuss each element in detail:

#### $$Q$$

This is the set of states that the Turing Machine can be in through its execution lifecycle.&#x20;

#### $$\Sigma$$

The letters that be written on the tape as input. It is important to note that the blank symbol \_ *cannot* be a part of the input alphabet.

#### $$\Gamma$$

The set of all letters that the Turing Machine is able to use (i.e. read/write). Note that $$\Sigma \subseteq \Gamma$$.&#x20;

#### \_

The blank symbol.

#### $$F$$

The set of final states the Turing Machine can terminate with. An sample set of final states is $$F = {A, R}$$ where $$A$$ means accepted and $$R$$ means rejected.

#### $$\delta$$

Perhaps the most important component of the Turing Machine, $$\delta$$ can be mathematically defined as follows

$$
\delta: (Q, \Gamma) \to (Q, \Gamma, d)
$$

where $$d$$ is the direction the scanner is move in. In layman's terms, $$\delta$$ takes in the current tape cell alphabet and the current state and outputs the new state, tape cell alphabet, and direction to move in.

### Examples

While the formal specification of a Turing Machine might appear simple, we will introduce several examples to show the state transition mechanism of a Turing Machine.

#### $$0^n1^n$$

As a first example, we want to construct a Turing Machine that will accept any string $$x$$ where $$x \in {0^n1^n}$$ (i.e. any string that starts with $$n$$ 0s followed by $$n$$ 1s) and rejects all other strings.  Even for a simple task like this, creating a Turing Machine might appear intimidating. To start, let's begin by defining the "easier" components:

* $$\Sigma = {0, 1}$$
* $$\Gamma = {0, 1, x}$$ (here, $$x$$ acts as a letter to mark the cells that we have already read)
* $$F = {A, R}$$

Furthermore, we know that there is at least one state in $$Q$$ (let 0 be the starting state). We now begin by defining $$q$$; to help guide us, let's start by defining the desired logic of the Turing Machine from a high level:

* Start at the leftmost letter. If the leftmost letter is the blank symbol **\_**, then we are dealing with the string $$0^n1^n$$ and so we can accept. Otherwise, move right until we find the first 1.
* Once we find the first 1, cross it out (i.e overwrite it with an $$x$$) and shift left until we find a 0
  * Ignoring all crossed out letters, if we come across the blank symbol **\_**, this implies we have the string $$0^i1^j$$ where $$i < j$$ and so we reject
* After we find a 0, we cross it out and move right until we find the next 1
  * In the case that we come across the blank symbol **\_** (i.e. we have crossed out all the 1s), we move left one last time to make sure that we don't have any 0s remaining; if so, this implies we have the string $$0^i1^j$$ where $$i > j$$ and so we reject.
* Once we come across the blank symbol, we accept.

Now that we understand what our Turing Machine needs to do from a high level, we can implement  $$\delta$$:&#x20;

<table data-full-width="true"><thead><tr><th>Input: Current State</th><th>Input: Current Symbol</th><th>Output: New State</th><th>Output: New Letter</th><th>Output: Direction</th></tr></thead><tbody><tr><td>0</td><td>0</td><td>0</td><td>0</td><td>r</td></tr><tr><td>0</td><td>1</td><td>1</td><td>x</td><td>l</td></tr><tr><td>0</td><td>_</td><td>A</td><td>_</td><td>r</td></tr><tr><td>1</td><td>1</td><td>1</td><td>1</td><td>l</td></tr><tr><td>1</td><td>0</td><td>2</td><td>x</td><td>r</td></tr><tr><td>1</td><td>x</td><td>1</td><td>x</td><td>l</td></tr><tr><td>1</td><td>_</td><td>R</td><td>_</td><td>l</td></tr><tr><td>2</td><td>0</td><td>R</td><td>0</td><td>l</td></tr><tr><td>2</td><td>x</td><td>2</td><td>x</td><td>r</td></tr><tr><td>2</td><td>1</td><td>1</td><td>x</td><td>l</td></tr><tr><td>2</td><td>_</td><td>3</td><td>_</td><td>l</td></tr><tr><td>3</td><td>x</td><td>3</td><td>x</td><td>l</td></tr><tr><td>3</td><td>1</td><td>R</td><td>1</td><td>l</td></tr><tr><td>3</td><td>0</td><td>R</td><td>0</td><td>l</td></tr><tr><td>3</td><td>_</td><td>A</td><td>_</td><td>l</td></tr></tbody></table>

Below is a graph visualization of our Turing Machine that we just built:

<figure><img src="/files/p3UzcaVGVKB4t7ahhkdL" alt=""><figcaption><p>Courtesy of <a href="https://turingmachine.io/">https://turingmachine.io/</a></p></figcaption></figure>

If you want to run the Turing Machine yourself, go to <https://morphett.info/turing/turing.html> and copy the code below into the simulator:

```
; This program checks if the input string is of the format 0^n1^n
; Input: a string of 0s and 1s

; Machine starts in state 0.

; State

; State 0: read the leftmost symbol
0 0 0 r 0
0 1 x l 1
0 _ _ r halt-accept

; State 1: we found a 1, getting next 0
1 1 1 l 1
1 0 x r 2
1 x x l 1
1 _ _ l halt-reject

; State 2: repeating back to 1
2 x x r 2
2 1 x l 1
2 _ _ l 3

; State 3: final check
3 x x l 3
3 1 1 l halt-reject
3 0 0 l halt-reject
3 _ _ l halt-accept
```

#### $$0110$$

In this next example, we will construct a Turing Machine that will accept any string that has the substring 0110 and rejects otherwise. Like last time, we begin by defining the more basic components:&#x20;

* $$\Sigma = {0, 1}$$
* $$\Gamma = {0, 1}$$
* $$F = {A, R}$$

Next, we define the logic of our Turing Machine from a high level:

* Assuming that we start at the leftmost letter of the input string, we check if it is a 0. If not, we move right until we find the first 0.&#x20;
  * If the string is empty, we reject.
* Once we find the first 0, we then check the next letter
  * If the next letter is a 0, we keep moving right until we find the first 1
* Assuming the next letter was a 1, we then check if the next letter is a 1
  * If the next letter is a 0, we revert to finding the next 1.
* Again assuming the next letter was a 1, we then check if the next letter is a 0
  * If the next letter is a 1, we start over
  * If the next letter is a 0, we accept&#x20;

We now define $$\delta$$:&#x20;

<table data-full-width="true"><thead><tr><th>Input: Current State</th><th>Input: Current Symbol</th><th>Output: New State</th><th>Output: New Letter</th><th>Output: Direction</th></tr></thead><tbody><tr><td>0</td><td>0</td><td>1</td><td>0</td><td>r</td></tr><tr><td>0</td><td>1</td><td>0</td><td>1</td><td>r</td></tr><tr><td>0</td><td>_</td><td>R</td><td>_</td><td>r</td></tr><tr><td>1</td><td>0</td><td>1</td><td>0</td><td>r</td></tr><tr><td>1</td><td>1</td><td>2</td><td>1</td><td>r</td></tr><tr><td>1</td><td>_</td><td>R</td><td>_</td><td>r</td></tr><tr><td>2</td><td>0</td><td>3</td><td>0</td><td>r</td></tr><tr><td>2</td><td>1</td><td>1</td><td>1</td><td>r</td></tr><tr><td>2</td><td>_</td><td>R</td><td>_</td><td>r</td></tr><tr><td>3</td><td>0</td><td>0</td><td>0</td><td>r</td></tr><tr><td>3</td><td>1</td><td>A</td><td>1</td><td>r</td></tr><tr><td>3</td><td>_</td><td>R</td><td>_</td><td>r</td></tr></tbody></table>

The graph visualization of the Turing Machine we just built is as follows:

<figure><img src="/files/VD232SMlVyStnWKEKYFk" alt=""><figcaption><p>Courtesy of <a href="https://turingmachine.io/">https://turingmachine.io/</a></p></figcaption></figure>

Finally, to run the Turing Machine yourself, go to  <https://morphett.info/turing/turing.html> and copy the code below into the simulator:

```
; This program checks if the input string has the substring 0110
; Input: a string of 0s and 1s

; Machine starts in state 0.

; State

; State 0: read the leftmost symbol
0 0 0 r 1
0 1 1 r 0
0 _ _ r halt-reject

; State 1: check for possible 1
1 0 0 r 1
1 1 1 r 2
1 _ _ r halt-reject

; State 2: check for possible 1
2 1 1 r 3
2 0 0 r 1
2 _ _ r halt-reject

; State 3: check for possible 0
3 1 1 r 0
3 0 0 r halt-accept
3 _ _ r halt-reject
```

### Again, Why Learn About Turing Machines?

As the two examples above might have shown, creating Turing Machines is an art in itself. However, some of you may still be wondering why this is helpful to learn when learning about smart contract development.

A smart contract, just like a Turing Machine, consists of both states and a deterministic state transition function. The state of a smart contract is comprised of the following (non-exhaustive) attributes:

* Account Nonce
* State Variables
* Account Balance

while the state transition function of a smart contract is comprised of its *bytecode*. When developing smart contracts, it is important to view the smart contract from a theoretical point-of-view and to consider all possible inputs/outputs and most importantly, what states the smart contract can be in; for to ignore this, one is putting their smart contract at risk of being exploited as a result of a smart contract being in an unintended state.

### Resources

* Philosophical Perspective on Turing Machines: <https://plato.stanford.edu/entries/turing-machine/>
* Cornell CS4820 (Intro to Algorithms) Handout: <http://www.cs.cornell.edu/courses/cs4820/2018sp/handouts/turingm.pdf>


# EVM Data Structures

What the EVM Stores

Having viewed both the Cuzco Model and the Turing Machine, we have finally arrived at the main focus of this chapter - learning about the architecture of the EVM. We begin by focusing on the components that define the state of the EVM at the time of execution (i.e. at the time of executing a transaction).&#x20;

In this section, we will go over the following data structures:

* Stack
* Memory
* Storage
* Contract Code
* Program Counter
* Gas Counter

{% hint style="info" %}

#### Bit Terminology

The following terminology is useful when reading about the EVM:

* 1 byte = 8 bits
* 32 bytes = 256 bits
* 1 word = 32 bytes
  {% endhint %}

### Stack

Like the stack we previously saw in the Cuzco Model, the stack that the EVM uses is a last-in, first-out data structure. Each stack element is a 32-byte integer and the stack has a maximum depth of 1024 elements.&#x20;

The stack is local relative to a message; whenever the EVM switches to a different message, the stack does not persist.

### Memory

Memory in the EVM is a byte-addressed data structure where each memory slot is initialized to 0. In theory, memory has a maximum size of $$2^{256}$$ bytes; in reality, memory expands throughout the execution of a message.

Memory is local relative to a message; whenever the EVM switches to a different message, the stack does not persist.

### Storage

Storage is where the state variables of a smart contract lives. For a given smart contract, its account storage is a mapping from 32-byte integers to 32-byte integers. EOAs, meanwhile, do not have any type of storage mapping.

What differentiates storage from memory and the stack is that storage is **persistent** - an account's storage mapping and any respective modifications to it persist throughout messages, transactions, and, in general, state transitions of the blockchain.&#x20;

### Contract Code

Like all programs, there needs to be a set of instructions that the EVM needs to execute. The contract code (also known as read-only memory (ROM or contract bytecode), is the set of instructions that the EVM executes when said contract is called. EOAs do not have any code associated with it.

As suggested by the term ROM, a smart contract's code is *immutable - once initialized, it cannot be changed.*

### Program Counter

The program counter (PC) tells the EVM what line of the contract bytecode to execute. We will focus more on the PC when we discuss operation codes

### Gas Counter

The gas counter tracks how much gas the current context of the EVM has left. If there is not enough gas, the transaction will revert.


# Operation Codes (Opcodes)

What the EVM Can Do

In the previous section, we went over the data structures that define the state of the EVM at any point of time. While we might have a grasp at their implementation, what we haven't yet covered is how we can manipulate these data structures.&#x20;

In this section, we will go over opcodes - the set of all instructions supported by the EVM which allows us to manipulate the data structures previously mentioned. The number of opcodes available to use is large; therefore, we will go over opcodes by identifying the different grouping of opcodes available to us. In particular, we will go over opcodes in the following order:

* Opcodes related solely to the stack
* Opcode related to memory
* Opcodes related to storage
* Arithmetic Opcodes
* Logical & Bitwise Opcodes
* Message Opcodes
* Environment Opcodes
* Event Opcodes
* Contract Creation, Return, and Miscellaneous Opcodes

### Stack-Based Opcodes

As it will become evident throughout the rest of this section, the stack is the main data structure for which we will extract the arguments for other opcodes. This brings up a good first question - how do we actually get items onto the stack? We will look at our first opcode for this - the PUSH1 opcode. The PUSH1 opcode has the following syntax (in hexadecimal notation):

```
60 <arg>
```

where `arg` is the value to push onto the stack. The next logical question to ask is what value can I set `arg` to? To this, look at the integer attached to `PUSH1`. The `1` indicates that `arg` is at most a 1-byte value. Therefore, the `PUSH1` opcode allows us to push a 1-byte value onto the stack.&#x20;

If you recall from the [EVM Data Structures](/understanding-the-evm/the-ethereum-virtual-machine/evm-data-structures) section, each element on the stack is 32-byte long. Therefore, we are not limited to just pushing 1-byte values onto the stack. In fact, for any `1 <= n <= 32`, there exists a `PUSHn` opcode.&#x20;

While pushing values onto the stack allows us to feed in arguments to the EVM, consider the following scenarios:

* What if we want to switch the position of an element on the stack?
* What if we wanted to duplicate elements on the stack?

To address both of these scenarios, we have the `SWAP` and the `DUP` opcodes which, as their abbreviations might suggest, allow us to swap and duplicate stack items, respectively.

### Memory-Based Opcodes

### Storage-Based Opcodes


# Gas


# Contract Compilation


# Contract Runtime


# Gas Optimizations


# Yul

Solidity's Sophisticated Brother

The entire content of this textbook can be summarized by the following word: Solidity.

Okay, maybe that's not the case. After all, we spent an entire chapter learning about the mechanics of the EVM. Learning about the EVM helped us understand how our code is actually compiled/run and gave us the foundation to write code that is both more secure and more efficient. However, one can argue that this still catered towards the overarching theme that is Solidity.&#x20;

In this chapter, we will diverge from learning strictly about Solidity and begin exploring **Yul**, a "middle-level" programming language that allows us to write code that is "closer" to the EVM when compared to Solidity. Yul is written within Solidity and offers reductions in gas usage when written correctly. However, as it will become apparent in the later parts of this chapter, Yul opens the door for advanced smart contract functionality and phenomena that will make you question your understanding of EVM-compatible blockchains (in a good way, of course).


# Metamorphism


# Bitwise Manipulations


# Security


# Types of Testing


# Why ERCs?


# ERC20


# ERC721


# ERC777


# ERC1155


# OpenZeppelin


# Uniswap


# Multisignature Contracts


# AAVE/Compound


# Consensus Mechanisms vs Sybil Resistance Mechanisms


# Maximal Extractable Value (MEV)


# Looking Past The EVM


# Developer Practices


# Spring 2023 Past Resources


