# on some of my favorite openzeppelin smart contracts

By [bt3gl's symposium](https://paragraph.com/@go-outside) · 2023-08-31

---

tl; dr
------

today i go over some [openzeppelin contracts](https://github.com/OpenZeppelin/openzeppelin-contracts) while discussing features and vulnerabilities (such as reentrancy and ownership).

this post is suitable for web2|3 hackers, solidity or non-solidity peeps, and computer nerds in general 🤓. for a general intro to solidity, you can check my [web3-starter-sol](https://github.com/autistic-symposium/web3-starter-sol).

* * *

**👾 today’s outline**
----------------------

    000. an open and secure zeppelin 
    
    001. utils/ 
        - Context.sol: a wrapper for msg.sender and msg.data
        - Array.sol: handy methods for arrays
    
    010. access/
        - Ownable.sol: providing onlyOwner modifier
        - Ownable2Step.sol: an example of inheriting Ownable
    
    011. security/
        - ReentrancyGuard.sol: guard against single function reentrancy attacks
    
    100. proxy/
        - Proxy.sol: implementing core low-level delegation functionality
    

* * *

**🎶 today’s mood**
-------------------

[https://open.spotify.com/track/5AgIGlGECjfquInIs1olEf?si=32af65d2803f4dbc](https://open.spotify.com/track/5AgIGlGECjfquInIs1olEf?si=32af65d2803f4dbc)

* * *

000\. an open and secure zeppelin
---------------------------------

founded in 2015, [openzeppelin](https://www.openzeppelin.com/) is an awesome crypto cybersecurity company that has created essential standards for secure blockchain applications.

besides [security audits,](https://www.openzeppelin.com/security-audits) their main products are the [defender platform (now 2.0)](https://docs.openzeppelin.com/defender/v2/), the [forta network](https://forta.org/), and the widely used [smart contracts libraries](https://docs.openzeppelin.com/contracts/4.x/) (containing [implementations of standards such as ERC20 and ERC721](https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token), [access control and role-based schemes](https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/access), [reusable solidity components](https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/utils), etc.). they also have a vast smart contract documentation ([for example, on things like how to extend their contracts via inheritance](https://docs.openzeppelin.com/contracts/4.x/extending-contracts)) and [the solidity wizard tool.](https://wizard.openzeppelin.com/)

diving into their [github](https://github.com/OpenZeppelin), there are several projects worth an in-depth review. for instance, my next post will be about my solutions to [their ctf game (called ethernaut)](https://ethernaut.openzeppelin.com/), using a systematic methodology with [foundry](https://github.com/foundry-rs/foundry) (you can take a peak already [at my github](https://github.com/go-outside-labs/ethernaut-foundry-detailed-solutions-sol)).

however, today we will be talking about some of [openzeppelin’s smart contracts](https://github.com/OpenZeppelin/openzeppelin-contracts), inside `util/`, `access/`, `security/`, and `proxy/`:

    |----- contracts/
              |
              |----- finance/
              |----- governance/
              |----- interfaces/
              |----- metatx/
              |----- mocks/
              |----- token/
              |----- ✨👀✨ utils/
              |----- ✨👀✨ access/
              |----- ✨👀✨ security/
              |----- ✨👀✨ proxy/
    

\[ perhaps another time we will look at `finance/` (_e.g.,_ `VestingWallet.sol`) or `governance/` (_e.g.,_ `TimelockController.sol` and `Governor.sol`), or the token interfaces, `metatx/` (_e.g.,_ `ERC2771*.sol`), `mock/`, `token/` (_e.g.,_ `ERC1155`, `ERC20`, `ERC72`), etc… as they are all relevant and fun \].

* * *

001\. utils/
------------

    |----- utils/
              |
              |----- cryptography/
                        |----- ECDSA.sol
                        |----- EIP712.sol
                        |----- MerkleProof.sol
                        |----- MessageHashUtils.sol
                        L----- SignatureChecker.sol
              |----- introspection/
                        L----- ERC165.sol
              |----- math/
                        |----- Math.sol
                        |----- SafeCast.sol
                        L----- SignedMath.sol
              |----- structs/
                        |----- BitMaps.sol
                        |----- Checkpoints.sol
                        |----- DoubleEndedQueue.sol
                        |----- EnumerableMap.sol
                        L----- EnumerableSet.sol
              |----- Base64.sol
              |----- Address.sol
              |----- Create2.sol
              |----- Multicall.sol
              |----- Nonces.sol
              |----- ShortStrings.sol
              |----- StorageSlot.sol
              |----- Strings.sol
              |----- ✨👀✨ Arrays.sol
              L----- ✨👀✨ Context.sol
    

`utils/` are miscellaneous contracts and libraries containing utilities for new data types, security, and safe low-level primitives.

before we look at `Ownable`, `Proxy`, and `ReentrancyGuard,` we will look at `Context` (as it is a short && sweet inherited suite) and to `Array` (as we can talk a little bit about one of my favorite subjects: **algorithms**).

for completeness, the other contracts in this directory are:

*   the libraries `Address`,`Base64`, and `Strings`, providing operations related to the native data types.
    
*   `SafeCast` providing ways to convert between `signed` and `unsigned` types safely.
    
*   `Multicall` providing a way to batch together multiple calls in one external call.
    
*   `EnumerableMap` is an extension of `mapping` with key-value enumeration (and iteration). same for `EnumerableSet`.
    
*   `Create2` provides utilities to safely use the EVM `CREATE2` opcode without dealing with low-level assembly.
    

* * *

### **utils/Context.sol**

`Context` is an `abstract` [wrapper for the current execution context](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Context.sol), _i.e.,_ the transaction sender and data.

> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> 
> **_contracts must be marked as_** `abstract` **_when at least one of their functions is not implemented or they do not supply arguments for their base contract constructor. they cannot be instantiated directly or override an implemented virtual function with an unimplemented one._**
> 
> `abstract` **_contracts can be helpful in the same logic as defining methods through an interface is._**
> 
> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

![](https://storage.googleapis.com/papyrus_images/8c04c40902758527a1f0c0342a9e119cedb77b060bf0aeb4adaecc49516ca1f2.png)

this simple contract creates two `internal` `view` `virtual` functions for both `msg.sender` and `msg.data`.

> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> 
> `internal` **_functions can only be accessed from inside the current contract or contracts deriving from it. they cannot be accessed externally and are not exposed through the contract’s ABI._**
> 
> `external` **_functions are part of the interface and can only be called from other contracts and transactions. they cannot be called internality (except with_** `this`).
> 
> `public` **_functions are part of the contract interface and can be either called internally or with message calls._**
> 
> `virtual` **_means that the function can change its behavior in derived (overriding) classes (the keyword_** `override` **_must be used in the overriding modifier)._**
> 
> `view` **_functions declare that no state will be changed. when a_** `view` **_function is called, the_** `STATICALL` **_opcode is used (enforcing the state to stay unmodified). for library_** `view` **_functions,_** `DELEGATECALL` i**_s used (there are no runtime checks that prevent state modifications). a_** `view` **_function_** [**cannot modify the state of the contract**](https://docs.soliditylang.org/en/v0.8.15/contracts.html#view-functions), **_for instance by writing to state variables, creating other contracts, emitting events, sending ether with_** `call()` **_or using any low-level calls, using_** `selfdestruct()`, **_calling functions that_** `pure` **_or_** `view`**_, or using inline assembly with certain opcodes._**
> 
> `pure` **_functions declare that no state variable will be changed or read (i.e., it promises not to modify or read from the state). it’s possible to evaluate a_** `pure` **_function at compile-time given only its inputs and_** `msg.data` **_(without knowledge of the blockchain state)._**
> 
> **_to understand visibility and getters, check_** [**_solidity by example._**](https://solidity-by-example.org/visibility/)
> 
> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

in summary, `Context` is an excellent example of how openzeppelin engineers design their code to be more generic and upgradeable.

for instance, if `msg.sender` becomes obsolete (such as `tx.origin`), this primitive can be updated in this one-liner instead of across all contracts _in the world._

* * *

### **utils/Array.sol**

an `Array` type in solidity has [a compile-time fixed size or a dynamic size,](https://solidity-by-example.org/array/) and can be declared several ways:

    uint[] public array;
    uint[] public array = [1, 3, 7];
    uint[10] public array;
    

openzeppelin’s `Array` is a `library` [that provides a collection of functions related to array types](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Arrays.sol).

it utilizes `utils/StorageSlot.sol` (for reading and writing primitive types to specific storage slots) and `util/math/Math.sol` (for math utilities missing in solidity).

![](https://storage.googleapis.com/papyrus_images/f3f25e4dcd0220c79b98129bc5da05e9600cf67dc9a9f4f633890c1d8b907719.png)

> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> 
> **_libraries are like contracts, but without any state variable or payable function._**
> 
> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

the `internal` `view` `findUpperBound()` function is an `O(log n)` search method for (ascending order and non-repeated) sorted arrays, which returns the first index that contains a value greater or equal to `element`:

![](https://storage.googleapis.com/papyrus_images/c20fe905676b3d47ad96570922393a1de54b31528f917a4687699d60e5a7bb5e.png)

💜 **this is just our good and old binary search!** 💜

if python is more familiar to you, here is a classical implementation of this algorithm:

![](https://storage.googleapis.com/papyrus_images/1e764161192eb72b1ca962f07147c40945b557a98020549098f83fc5711b896d.png)

mostly, when dealing with binary search algorithms, you want to pay attention to the three possible “templates”:

1.  `while left < right`, with `left = mid + 1` and `right = mid - 1`.
    
2.  `while left < right`, with `left = mid + 1` and `right = mid`, and `left` is returned.
    
3.  `while left + 1 < right`, with `left = mid` and `right = mid`, and `left` and `right` are returned.
    

> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> 
> **_if you are not familiar with algorithms and data structure, i have an_** [**_entire repository teaching these subjects with detailed examples,_**](https://github.com/go-outside-labs/master-algorithms-py/tree/master) **_based on_** [**_a book i published many years ago_**](https://www.hanbit.co.kr/store/books/look.php?p_code=B8465804191)**_. you should check it out. ( :_**
> 
> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

in the case of `Array`, we are looking at the second template:

*   `mid` will always be less than high because `Math.average()` rounds towards zero (integer division with truncation).
    
*   `low` is the exclusive upper bound.
    
*   the inclusive upper bound is `return`.
    

the next three `internal` `pure` `unsafeAccess()` functions deal with the `assembly` code that calculates the storage slot of the element at the given index `pos`, for either `address[]`, `bytes32[]`, or `uint256[]` data structures.

> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> 
> **_the types_** `bool`, `uint256`, `int256`**_, and_** `address` **_are some of the primitive types available in solidity._**
> 
> `mappings` **_are non-iterable maps where the keys can be a built-in type,_** `bytes`, `string`, **_or even a contract._**
> 
> **_the value types_** `bytes1`, `bytes2`,**_…,_** `bytes32` **_hold sequences of bytes from one to up to_** `32`. **_the type_** `bytes32[]` **_is an array of bytes (the same way_** `uint256[]` **_is an array o_**f `uint256`**_, etc.)._**
> 
> **_by the way, due to padding rules,_** `bytes*` **_with anything less than_** `32` **_is a waste (and should be replaced by_** `bytes`, **_a dynamically-sized byte array)._**
> 
> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

![](https://storage.googleapis.com/papyrus_images/a6da68caa83ed6ff8944f0cd7aa8083d1f9e9ef820f8fb7debbe00a82b5b00e5.png)

> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> 
> **_the language used for inline assembly in solidity is called_** [**_yul_**](https://docs.soliditylang.org/en/v0.8.21/yul.html#yul) **_and offers a way to access the EVM at a low level, bypassing safety features and checks._**
> 
> `mstore()` **_represents the opcode_** `MSTORE` [**_that writes a_**](https://www.ethervm.io/#MSTORE) `(u)int256` **_to memory (i.e., volatile read-write RAM which is not persistent across transactions)._**
> 
> `keccak256(bytes memory)` **_is a cryptographic function that computes the_** [**_keccak-256 hash_**](https://solidity-by-example.org/hashing/) **_of the input (and is used all over ethereum, for example, for creating deterministic unique IDs, commit-reveal schemes, and compact signatures)._**
> 
> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

finally, two `internal` `pure` `unsafeMemoryAccess()` functions deal with memory access unsafely by skipping solidity’s index-out-of-range check.

they should only be used if the input index `pos` is lower than the array length:

![](https://storage.googleapis.com/papyrus_images/23a8b7fa60ce4816e4d1b9391ead61e877959e9b88f8ab8cbf61bca676213186.png)

* * *

010\. access/
-------------

    |----- access/
          |
          |----- ✨👀✨ Ownable.sol
          |----- ✨👀✨ Ownable2Step.sol
          |----- AccessControl.sol
          L----- extensions/
    

in general, access control is given through ownership, where an account is assigned as the contract’s `owner`.

the `access/` subdirectory provides libraries to restrict who can access the functions of a contract or when they can do it.

while `AccessControl` states general role-based access control features, `Ownable` implements a simple mechanism with a single owner that can be assigned to a single account.

* * *

### access/Ownable.sol

let’s look at the `Ownable` [contract](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol) and how an account (`owner`) can be granted access to specific functions through the `onlyOwner` modifier (which reverts any function that is not called by the address registered as `owner`).

> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> 
> **_modifiers are code that can be run before and/or after a function call. they are used to restrict access, validate inputs, and perform security checks._**
> 
> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

the main features of this `abstract` contract are:

*   defining the modifier `onlyOwner`.
    
*   defining a “special” address called `owner` , set to the address provided by the deployer.
    
*   defining how `owner` can be changed with `transferOwnership()`.
    

first, we see that the contract is inheriting from `utils/Context.sol` (discussed above) and has one `private` state variable, `_owner` (remember, state variables are declared outside a function, [stored in storage](https://docs.soliditylang.org/en/v0.8.21/internals/layout_in_storage.html), and updated through a transaction).

![](https://storage.googleapis.com/papyrus_images/c53bf59ae49d2a45edebff6d39a31586e27f5a48e58bbabc31f35ea06c7b0817.png)

then, the contract performs two sanity checks, throwing `error` if the caller account is not authorized to perform an operation or the `owner` is not a valid account (_e.g.,_ address `0x0`).

after that, the `OwnershipTransferred()` `event` is declared.

> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> 
> `event` **_is a convenient interface for abstracting the EVM logging protocol. their log entries provide the contract’s address, a series of up to four topics, and some binary data. events leverage the function’s ABI to interpret the typed structure, and can also be used as a cheap form of storage._**
> 
> `error` **_allows the definition of descriptive names and data for failure situations, with an opcode to abort execution and revert all state changes. errors can be thrown by calling_** `require`, `revert`, **_or_** `assert`.
> 
> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

next, `constructor` is called, and the contract is initialized, setting the address provided by the deployer as the initial `owner` (by default, `owner` is the account that deployed the contract).

> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> 
> **_a constructor is an optional function executed upon contract creation that can run contract initialization code. before it is executed, state variables are initialized (to their values or default values). after it has run, the final code of the contract is deployed to the blockchain._**
> 
> **_if there is no constructor, the contract will assume the default_** `constructor() {}`.
> 
> **_learn more about how_** `constructor` **_could be exploited in_** [**_my ethernaut solution for fal1out_**](https://github.com/autistic-symposium/ethernaut-foundry-framework-solutions-sol/tree/main/test/02)**_._**
> 
> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

![](https://storage.googleapis.com/papyrus_images/160af36ed39e199706395a60efd3ebf34464436f28a37d5dbde18c36a2e20775.png)

finally, we see the creation of the modifier `onlyOwner`, stating that the function must be called by the `owner`.

the `internal` `view` `virtual` checker `_checkOwner()` verifies that `msg.sender` is `owner`, and any other account reverts with `OwnableUnauthorizedAccount()`.

if the check pass, `_;` states that the function should execute normally as it will be replaced by the actual function body when the modifier is used.

> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> 
> **_the underscore “\_” is used inside\* a function modifier to tell solidity to \*execute the rest of the code._**
> 
> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

note that `_msgSender()` is a wrapper from `Context.sol`, the same way `owner()` is a wrapper for the state variable `_owner`.

![](https://storage.googleapis.com/papyrus_images/f2ad3eca889fdf9406eab3e335c7027fb97a8aecb6461478a0a451299a8f5fac.png)

the last part of this contract is two `public` and one `internal` functions to deal with ownership changes. note that the `public` functions are already modified by `onlyOwner`.

`renounceOwnership()` leaves the contract without `owner` and can only be called by the current `owner`.

`transferOwnership()` and `_transferOwnership()` transfer `owner` to a new account (`newOwner)`, emitting an `OwnershipTransferred()` `event`. they also can only be called by the current `owner`.

in conclusion, the modifier `onlyOwner` is seen in many projects as ownership can be assigned to a simple EOA, multiple EOAs, multisig accounts, or complex modules with timelocks and governance. just a few examples: [the address manager contract in optimistic ethereum](https://github.com/ethereum-optimism/optimism/blob/33cb9025f5e463525d6abe67c8457f81a87c5a24/packages/contracts/contracts/optimistic-ethereum/libraries/resolver/Lib_AddressManager.sol#L10), [the usdc stablecoin contract](https://etherscan.io/address/0xa2327a938febf5fec13bacfb16ae10ecbc4cbdcf#code), and [the chainLink price feeds contract](https://etherscan.io/address/0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419).

as a final example of its applicability, here is an example in an `AllowList`:

![](https://storage.googleapis.com/papyrus_images/dbe510c1be1c26ac0e05de6dab9b3a227e39f4004208f442fad3c9ad52285c6c.png)

the limitations of `Ownable` is that only one address can be `owner` at a given time, and only the `owner` gets to decide who is `newOwner`.

when different authorization levels are needed, [role-based access control (RBAC)](https://docs.openzeppelin.com/contracts/3.x/access-control) can define multiple roles, each allowed to perform different sets of actions (_e.g.,_ instead of `onlyOwner`, use `onlyAdminRole` in some places, and `onlyModeratorRole` in others).

openzeppelin provides the contract [Roles](https://docs.openzeppelin.com/contracts/2.x/api/access#Roles) and [AccessControl](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/AccessControl.sol) for such extensions.

* * *

### **access/Ownable2Step.sol:**

as an illustration, let’s take a quick look at another contract in the `access/` directory, whose parent is `Ownable`.

`Ownable2Step` provides an access control mechanism where there is an account (`owner`) with, now, two functions for ownership transferring, `transferOwnership()` and `acceptOwnership()`, for added safety of a securely designed two-step process:

![](https://storage.googleapis.com/papyrus_images/c49e971603f9fcaee71a602b8cc1d75bcaaa08419741c2ead915f18086b2dfb3.png)

* * *

011\. security/
---------------

    |----- security/
          |
          |----- Pausable.sol
          L----- ✨👀✨ ReentrancyGuard.sol
    

contracts inside `security/` seek to deal with common security practices:

*   `ReentrancyGuard` is a modifier that can prevent reentrancy for single contract functions.
    
*   `Pausable` is an emergency response mechanism that can pause a functionality if remediation is needed.
    

although `Pausable` is an interesting contract, today we will discuss the reentrancy module.

* * *

### **security/ReentrancyGuard.sol**

contracts may call other contracts by function calls or transferring ether. they can also call back contracts that called them (_i.e.,_ reentering) or any other contract in the call stack.

a reentrancy attack can happen when a contract is reentered in an invalid state. this can happen if the contract calls other untrusted contracts or transfers funds to untrusted accounts.

> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> 
> **_a state in the blockchain is considered valid when the contract-specific invariants hold true._**
> 
> **_contract invariants are properties of the program state that are expected always to be true. for instance, the value of_** `owner` **_state variable, the total token supply, etc., should always remain the same._**
> 
> [**_to learn more, @andrzej has a great post on contract, preconditions, & invariants._**](https://akrzemi1.wordpress.com/2020/12/09/contracts-preconditions-invariants/)
> 
> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

in its simplest version, an attacking contract exploits vulnerable code in another contract to seize the flow of operation or funds.

for example, an attacker could repeatedly call a `withdraw()` or `receive()` function (or similar balance updating function) before a vulnerable contract’s balance is updated.

here is a step-by-step scenario:

1.  a vulnerable contract has `X ether`.
    
2.  an attacker stores `1 ether` with a `deposit()` function.
    
3.  an attacker calls the `withdraw()` function and points the recipient address to their malicious contract.
    
4.  the `withdraw()` function verifies that the attacker has `1 ether` (yes, from their deposit) and transfers `1 ether` to the malicious contract.
    
5.  the `fallback()` function is triggered when the `ether` is received and, before `balances` is updated, calls `withdraw()` repeatedly until the vulnerable contract is drained (`X + 1 ether` is sent to the attacker).
    

![](https://storage.googleapis.com/papyrus_images/f81336fded78f0b50d770de9cdfc4656a61e5a203d66dd28dc32873b24c171ee.png)

reentrancy attacks can happen on a single function, multiple functions, or even extend across distinct smart contracts:

*   **single reentrancy attacks** can occur when a vulnerable function is the same function the attack is trying to call recursively.
    
*   **cross-function attacks** can occur when two or more functions (including a vulnerable one) share the same state variables. they are harder to detect and prevent.
    
*   **cross-contract attacks** can occur when contracts share the same state (_e.g._, if multiple contracts share the same variable and a contract updates it insecurely). an example could be the exploitation of an [ERC-777](https://eips.ethereum.org/EIPS/eip-777) token, which enables “operators” to send tokens on behalf of a token owner by adding `send()` and `receive()` hooks ([this was exploited on the uniswap/lendf.me attack](https://peckshield.medium.com/uniswap-lendf-me-hacks-root-cause-and-loss-analysis-50f3263dcc09)).
    

> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> 
> [**_on june 17th, 2016, ”The DAO” was compromised_**](https://github.com/pcaversaccio/reentrancy-attacks/issues/1)**_, and 3.6 million ETH were stolen using a single-function reentrancy attack. the ethereum foundation had to issue a critical update to reverse the hack, leading to its fork._**
> 
> **_recent reentrancy hacks were_** [**_plx locker (2021)_**](https://polydex.medium.com/plx-locker-smart-contract-incident-post-mortem-75342124a3e8)**_,_** [**_cream finance (2021)_**](https://www.halborn.com/blog/post/explained-the-cream-finance-hack-october-2021)**_,_** [**_rari capital (2021)_**](https://nipunp.medium.com/5-8-21-rari-capital-exploit-timeline-analysis-8beda31cbc1a)**_,_** [**_siren protocol (2021)_**](https://www.halborn.com/blog/post/explained-the-siren-protocol-hack-september-2021)**_,_** **_paraluni (2022)_****_,_** [**_revest finance (2022)_**](https://therecord.media/2-million-stolen-from-defi-protocol-revest-finance-platform-unable-to-reimburse-victims)**_,_** [**_fei protocol (2022)_**](https://certik.medium.com/fei-protocol-incident-analysis-8527440696cc)**_,_** [**_ola finance (2022)_**](https://www.coindesk.com/tech/2022/03/31/ola-finance-exploited-for-36m-in-re-entrancy-attack/)**_, and_** [**_safemint(2022)_**](https://blocksecteam.medium.com/when-safemint-becomes-unsafe-lessons-from-the-hypebears-security-incident-2965209bda2a)**_._**
> 
> **_i also highly recommend_** [**_@pcaversaccio historical collection of reentrancy attacks._**](https://github.com/pcaversaccio/reentrancy-attacks)
> 
> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

if it still feels a bit abstract, don’t worry. let’s try again with code.

consider the following `Vulnerable` contract with a `public` `withdraw()` function, where an external `call()` aims to transfer (withdraw) ETH to `msg.sender`:

![](https://storage.googleapis.com/papyrus_images/47f2247de9266836db39382dddbdf26db90722cfd57716b9406f41bfd7be4ed6.png)

the contract invariant is that its total amount of funds should equal the sum of all entries in `balances`.

however, because the user’s balance is updated to `0` ONLY after `call()`, the invariant is broken because `amount` has been sent, but `balances` has not been updated yet.

if `msg.sender` is another smart contract, it can, as we saw above, recall `withdraw()` function through `fallback()` or `receive()` (they would be triggered when `ether` is sent to the attacker’s contract).

since `balances` is not updated until after the call, the reentering invocation of `withdraw()` can be repeated and drain the contract.

![](https://storage.googleapis.com/papyrus_images/239b1e84cd315e42ffba9f3f19c8281d77117a9de41006e7685b78fce9cc5ab3.png)

> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> 
> **_solidity’s_** `fallback()` **_function is executed if none of the other functions match the function identifier or no data was provided with the function call. it can be optionally_** `payable`.
> 
> `receive()` i**_s a new keyword in solidity 0.6.x, and it is used as a_** `fallback()` **_function for empty_** `calldata` **_(or any value) that is only able to receive_** `ether`.
> 
> **_to learn more about_** `fallback()` **_exploitation, check_** [**_this ethernaut solution_**](https://github.com/go-outside-labs/ethernaut-foundry-detailed-solutions-sol/tree/main/test/01)**_._**
> 
> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

in this exploit, the only checks the attacker needs are:

1.  the accumulated gas cost, and
    
2.  the overall balance of the target smart contract (to ensure they are not attempting to withdraw more `ether` than the contract holds, as it would revert the entire transaction and change the state).
    

> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> 
> **_but, what is gas, really?_**
> 
> **_gas is a unit of computation. each transaction is charged with some gas that has to be paid for by the originator._**
> 
> **_gas spent is the total amount of gas used in a transaction. if the gas is used up at any point, an out-of-gas exception is triggered, ending execution and reverting all modifications made to the state in the current call frame. since each block has a maximum amount of gas, it also limits the work needed to validate a block._**
> 
> **_gas price is how much ether you are willing to pay for gas. it's set by the originator of the transaction, who has to pay_** `gas_price * gas` **_upfront to the EVM executor (and any gas left is refunded, except for exceptions that revert changes)._**
> 
> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

so what’s the solution for reentrancy?

initially, single-function reentrancy vulnerabilities could be fixed by deferring the external call until after `balances` has been updated:

![](https://storage.googleapis.com/papyrus_images/166e132bb36cc76f984be5028c9f1576f3dbae58da17666ae543ebda43ba7a1d.png)

however, this solution also has issues. if another function calls `withdraw()`, the attack could still be possible through cross-function reentrancy (_i.e.,_ when multiple functions share the same state).

for instance, an attacker could reenter the contract with a `transfer()` function while `withdraw()` call has not yet concluded (and `balances` for `msg.sender` has not been set to `0` yet):

![](https://storage.googleapis.com/papyrus_images/19a3fdb780ee33e0ac157d29a293db6c96574002d026bcf95d0b531e56a4271d.png)

a remediation is to defer ANY external call until after all relevant state changes have occurred.

✨ enters openzeppelin’s `ReentrancyGuard` ✨

the contract allows a universal solution to reentrancy protection for individual contracts through a [mutex technique](https://en.wikipedia.org/wiki/Lock_\(computer_science\)) (_i.e.,_ working with lock states that only `owner` can change):

![](https://storage.googleapis.com/papyrus_images/90559b30e5a49b08308fca805c200a6fc18de574f216a0d1ffbcf683bf24bc65.png)

by using the modifier `nonReentrant`, other contracts could safely consume `NotVulnerable`, using `public` `balances` for its logic while `withdraw()` safeguarded against reentrancy.

> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> 
> **_to learn from a reentrancy practical problem, check_** [**_my ethernaut foundry solution for this vulnerability_**](https://github.com/go-outside-labs/ethernaut-foundry-detailed-solutions-sol/tree/main/test/10)**_. here is a teaser of the exploit i wrote:_**
> 
>     contract ReentrancyExploit {
>     
>         Reentrance private level;
>         bool private _ENTERED;
>         address private owner;
>         uint256 private initialDeposit;
>     
>         constructor(Reentrance _level) {
>             owner = msg.sender;
>             level = _level;
>             _ENTERED = false;
>         }
>     
>         function run() public payable {
>             require(msg.value > 0, "must send some ether");
>             initialDeposit = msg.value;
>             level.donate{value: msg.value}(address(this));
>             level.withdraw(initialDeposit);
>             level.withdraw(address(level).balance);
>         }
>     
>         function withdrawtoHacker() public returns (bool) {
>             uint256 hackerBalancer = address(this).balance;
>             (bool success, ) = owner.call{value: hackerBalancer}("");
>             return success;
>         }
>     
>         receive() external payable {
>             if (!_ENTERED) {
>                 _ENTERED = true;
>                 level.withdraw(initialDeposit);
>             }
>         }
>     }
>     
> 
> **_i also highly recommend you check_** [**_openzeppelin’s post on the reentrancy guard after istanbul hard fork and eip-1884._**](https://blog.openzeppelin.com/reentrancy-after-istanbul)
> 
> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

now let’s take a deeper look at `ReentrancyGuard`. the `abstract` contract `ReentrancyGuard` starts with:

*   three `private` `uint256` state variables. the first two, `_NOT_ENTERED` and `_ENTERED`, serve as (a cheap) `bool`.
    
*   a general `error`, and
    
*   a `constructor` that starts with `_status` equal to `false` (meaning, “not entered yet” or `false`) when the contract is deployed.
    

![](https://storage.googleapis.com/papyrus_images/22d3c9158feb4be59f387a6f7eedfc4548350548a2ad557f5e5b41955b48a894.png)

> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> 
> `ReentrancyGuard` **contains a _docstring_ explaining why** `_NOT_ENTERED = 1` **_and_** `_ENTERED = 2`:
> 
> **_“_**`bool` **_are more expensive than_** `uint256` **_or any type that takes up a full word because each write operation emits an extra_** `SLOAD` **_to first read the slot's contents, replace the bits taken up by the_** `bool`**_, and then write back. this is the compiler's defense against contract upgrades and pointer aliasing, and it cannot be disabled._**
> 
> **_the values being non-zero value makes deployment a bit more expensive, but in exchange the refund on every call to_** `nonReentrant` **_will be lower in amount. since refunds are capped to a percentage of the total transaction's gas, it is best to keep them low in cases like this one, to increase the likelihood of the full refund coming into effect.”_**
> 
> **edit:** [**PaulRBerg**](https://github.com/PaulRBerg) **was** [**curious about this too**](https://forum.openzeppelin.com/t/understanding-the-refund-logic-in-reentrancyguard/37948/2)**.**
> 
> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

following is the definition of the modifier `nonReentrant`, which we learned above that can be utilized on inherited contracts to prevent a function from being called while this function is still executing (_i.e.,_ ensure that there are no nested/reentrant calls to them).

this modifier leverages two `private` functions, `_nonReentrantBefore()` and `_nonReentrantAfter()`, to ensure that any call in between (the contract execution) reverts with `ReentrancyGuardReentrantCall()`. in other words, any call to `nonReentrant` after its first call will fail as there is already a `nonReentrant` function in the call stack.

note that once `_nonReentrantAfter()` is called and `_status` is restored to `_NOT_ENTERED`, [a refund can be triggered accordingly to EIP-2200.](https://eips.ethereum.org/EIPS/eip-2200) the EIP proposed a way for gas metering on `SSTORE`, so that subsequent storage write operations within the same call frame (such as reentry locks) can benefit from gas reduction.

![](https://storage.googleapis.com/papyrus_images/991b6b6e8f491cea66f78d7c652e50533aa318acdc8511f28a35654711132211.png)

> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> 
> **_i talked about_** `MSTORE` **_already, so it’s worth talking about the four ways the EVM stores data, depending on their context._**
> 
> **_firstly, there is the key-value stack, where you can_** `POP`, `PUSH` , `DUP1`, or `POP` **_data. basically, the EVM is a stack machine, as it does not operate on registers but on a virtual stack with a size limit_** `1024`**_. stack items (both keys and values) have a size of_** `32-bytes` (or `256-bit`), **_so the EVM is a 256-bit word machine (facilitating, for instance,_** **_keccak256 hash scheme and elliptic-curve computations)._**
> 
> **_secondly, there is the byte-array memory (RAM), used to store data during execution (such as passing arguments to internal functions). opcodes are_** `MSTORE` **_(like in_** `Array.sol`**_),_** `MLOAD`, or `MSTORE8`.
> 
> **_third, there is the calldata (which can be accessed through_** `msg.data`), **_a read-only byte-addressable space for the data parameter of a transaction or call. unlike the stack, this data is accessed by specifying the exact byte offset and the number of bytes to read. inline assembly functions to operate calldata are (they will be important in the next section):_**
> 
> *   `calldatacopy(t, f, s)`**_, used for opcode_** `CALLDATACOPY`, **_which copies a number of bytes of the transaction to memory (copies_** `s` **_bytes of calldata from position_** `f` **_to memory at position_** `t`**_)._**
>     
> *   `calldatasize()`, **_tells the size of transaction data._**
>     
> *   `calldataload()`**_, loads_** `32 bytes` **_of the transaction data onto the stack._**
>     
> *   **_additionally,_** `returndatacopy(t, f, s)` **_is used for opcode_** `RETURNDATACOPY` **_to copy_** `s` **_bytes from_** `returndata` **_at position_** `f` **_to memory at position_** `t`.
>     
> 
> **_lastly, there is disk storage, a persistent read-write word-addressable space, where each contract stores persistent information (and where state variables live), and is represented by a mapping of_** `2^{256}` **_slots of_** `32 bytes` **_each. the opcode_** `SSTORE` **_is used to store data and_** `SLOAD` **_to load._**
> 
> **_the required gas for disk storage is the most expensive, while storing data to stack is the cheapest._**
> 
> **_check_** [**_evm.storage_**](https://evm.storage/) **_for a dive deep into the storage of any contract on ethereum or avalanche and_** [**_ethervm.io_**](https://www.ethervm.io/) **_for a broad opcodes reference._**
> 
> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

what about [deadlocks](https://en.wikipedia.org/wiki/Deadlock)? what happens if a `nonReentrant` function calls another `nonReentrant` function?

because there is only a single `nonReentrant` guard, a `nonReentrant` function should always be external. the contract disclaimers that _“calling a_ `nonReentrant` _function from another_ `nonReentrant` _function is not supported. it is possible to prevent this from happening by making the_ `nonReentrant` _function external, and calling a_ `private` _function that does the actual work.”_

finally, the last part of the contract is the `internal` `view` function that returns `true` if the reentrancy guard is on:

![](https://storage.googleapis.com/papyrus_images/1d4d458439ae5f8d4cb8e2160cf5e3fc62c46ca6a87e2a074acdaba9ba399f45.png)

note that `ReentrancyGuard` is still vulnerable to cross-contracts attacks, where the execution flow of the external `call()` in `withdraw()` could still be taken over by an attacker (_e.g.,_ if they craft an exploit that presents a misleading `balances`).

check [this review](https://scsfg.io/hackers/reentrancy/) for a more detailed cross-contract reentrancy example using `Checkpoints` to monitor `balances`, for instance, visible to `AnotherContract` we discussed above.

to conclude this session, let’s go over other workarounds for reentrancy attack protection:

*   **the** [**checks-effects-interaction pattern**](https://docs.soliditylang.org/en/v0.8.21/security-considerations.html#use-the-checks-effects-interactions-pattern)**:**
    
    *   a technique to organize statements in a function so that the state remains valid before calling other contracts.
        
    *   basically, every statement is classified as either **check** (contract’s state change), **effect** (blockchain state change and writing to storage), or **interaction**, and must be in this exact order.
        
    *   however, this technique is prone to human error and would not work for multi-contract situations.
        

![](https://storage.googleapis.com/papyrus_images/1bdb2a637019dd4997b34ca47726ea9638e900dc8f266d824782df15c5229d71.png)

*   **using intermediary escrows (pull payments):**
    
    *   instead of sending funds to a receiver, they could be “pulled” out of an escrow contract.
        
    *   openzeppelin implements this pattern in the `PullPayment` [contract](https://docs.openzeppelin.com/contracts/3.x/api/payment#PullPayment).
        
*   **setting gas limits by utilizing** `transfer()` **or** `send()` **instead of** `call()`:
    
    *   the `fallback()` function in any contract is triggered by `transfer()` or `send()` .
        
    *   these functions only allow the receiver a stipend of `2300` gas sent that can be utilized in the execution of the contract, which is not enough to perform reentrancy attacks.
        
    *   [however, this argument has been debated by many advocates of stopping using](https://consensys.io/diligence/blog/2019/09/stop-using-soliditys-transfer-now/) `transfer()`, as gas/opcode pricing is not stable (this was touched on in the [EIP-1884 implementation](https://eips.ethereum.org/EIPS/eip-1884#motivation)).
        

* * *

**100\. proxy/**
----------------

    |----- proxy/
          |
          |----- ERC1967/
          |----- beacon/
          |----- transparent/
          |----- utils/
          |----- Clones.sol
          L----- ✨👀✨Proxy.sol
    

`proxy/` contracts provide a low-level implementation suite for different proxy patterns with and without upgradeability.

in another post, i will go over more details about different types of upgradeable proxies. today, we will take a quick look at `Proxy`.

* * *

### proxy/Proxy.sol

the main gist of the `abstract` [contract](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/Proxy.sol) `Proxy` is to provide a `fallback()` function that delegates all calls to another contract using the EVM opcode `DELEGATECALL`.

![](https://storage.googleapis.com/papyrus_images/b1df8d79f02d45774ffccc2a2398d75b824b91635117d8dda0982dae39b9359c.png)

`Proxy` does not contain any state variable and starts directly with the `internal` `virtual` function `_delegate()`, which runs inline assembly to delegate the current call to a second contract (given by `implementation`):

![](https://storage.googleapis.com/papyrus_images/b0d166121ee1a5a422ad57af1b73e60ccd8af41dac7a92580e4a9b1f52f423eb.png)

let’s see what the low-level code is doing:

1.  first, it takes full-control of memory to copy `msg.data` , writing at position `0`.
    
2.  then call `implementation` (the second contract address) with `delegatecall()` and copy the returned data.
    
3.  finally, revert if `result` is `0` (meaning an error) or return the data.
    

![](https://storage.googleapis.com/papyrus_images/da56d44dd33b6f8907c154d0b403e9910c9c4a4a67b8500862515a15ab4facec.png)

next, the `fallback()` function delegates the current call to the address returned by the `internal` `virtual` `_implementation()` function (an overridden function returning the address `fallback()` should delegate).

the `fallback()` function will return directly to the external caller, _i.e.,_ `success` and return data of the delegated call is returned to the caller of the proxy.

> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> 
> **_i mentioned the opcode_** `DELEGATECALL` **_before when talking about_** `view` **_modifiers for_** `library`.
> 
> `delegatecall()` **_is the low-level function for this opcode, similar to_** `call()`.
> 
> **_basically, when a contract_** `A` **_executes_** `delegatecall()` **_to contract_** `B`, `B`’**_s code is executed with contract_** `A`**_’ s storage,_** `msg.sender`**_, and_** `msg.value`.
> 
> **_to learn more about how attackers can exploit_** `DELEGATECALL`, **_check_** [**_my writeup for ethernaut’s delegation_**](https://github.com/go-outside-labs/ethernaut-foundry-detailed-solutions-sol/tree/main/test/06)**_._**
> 
> \~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

* * *

**◻️ motherofbots.eth**
-----------------------

---

*Originally published on [bt3gl's symposium](https://paragraph.com/@go-outside/on-some-of-my-favorite-openzeppelin-smart-contracts)*
