# Smart Contracts

## Overview

Herodotus smart contracts can be deployed on L1 and L2s, L3s and app-chains. In this documentation, we will focus on our deployments targetting L1 and L2.

{% hint style="info" %}
The black arrows represent smart contract calls that can mutate the state, and the green arrows are read-only calls.
{% endhint %}

<figure><img src="/files/g7u1rWRVM3HejYauVqFW" alt=""><figcaption><p>Herodotus Smart Contract Overview</p></figcaption></figure>

***

## Layer 1

### SHARP proofs aggregators factory

Factory contract for creating new `SharpFactsAggregator` contracts,

These contracts are created by `Clones`, which ensures they are upgradeable.

The purpose of using the Factory pattern is to have the ability to manage the creation and extension of multiple MMRs. By using this approach, we can simultaneously extend the tree with recent blocks by snapshotting a recent block hash and also extend the tree to the left by performing the cryptographic link check.

#### Notable Functions

| Function         | Description                                                                  |
| ---------------- | ---------------------------------------------------------------------------- |
| createAggregator | Orchestrates the creation of new, upgradeable SharpFactsAggregator contracts |

{% hint style="info" %}
All instances of the `SharpFactsAggregator`can be validated whether they have been created by the Factory since smart contract addresses on Ethereum are deterministic and can be computed by:

`keccak256(FACTORY_ADDRESS, factoryNonceAtCloneCreationTime)[0:20]`

Or by querying the variable stored in the Factory:

`aggregatorsById`
{% endhint %}

### SHARP proofs aggregator

The important functions from the perspective of E2E integration are:

#### Notable Functions

| Function           | Description                                                 |
| ------------------ | ----------------------------------------------------------- |
| getAggregatorState | Retrieves the current state of the SHARP proofs aggregator. |
| getMMRKeccakRoot   | Retrieves the MMR root of the MMR hashed using Keccak.      |
| getMMRPoseidonRoot | Retrieves the MMR root of the MMR hashed using Poseidon.    |
| getMMRSize         | Retrieves the current size of the MMR.                      |

### Messages sender

The **Messages Sender** contract is responsible for transmitting data(messages) to Layer 2.

The contract is used to send block hashes as well as the Poseidon or Keccak MMR roots to Layer 2. This is done by providing the identifier of the **`tree`**. It’s imperative that the contract confirms that the Factory has genuinely created the **`tree`** before proceeding with the transmission of the MMR roots.

### **Future work**

#### L2 Roots Cache

The L2 Roots Cache will be built off-chain.

***

## Layer 2

{% hint style="info" %}
The code shown is pseudocode. The exact code can be found on our [GitHub](https://github.com/HerodotusDev).
{% endhint %}

### Inbox/Dispatcher

The role of this contract is to receive and properly handle cross-rollup messages sent from the `L1: Messages sender`.

Depending on the received message, the contract will pass the call over to a different contract, which is why it acts as a dispatcher.

Each time it receives the message, it emits an event.

### Headers Store

The **Headers Store** contract is responsible for managing the Merkle Mountain Range (MMR), which stores the hashes of provably valid Ethereum block headers.

A key feature of the Header Store contract is its implementation of the Memento pattern, which lets the contract recall its past roots, ensuring that inclusion proofs remain indefinitely valid.

This contract also supports buffering or branching. New MMRs can be created and mutated independently from each other, allowing resource management and avoidance of global locks that would be required due to the non-homomorphic nature of hash-based accumulators.

Each branch of the MMR is given its own distinctive ID. Additionally, it's tagged with its present size to mark its version.

#### **Tree creation methods**

MMRs can be instantiated by calling one of these methods:

| Method                          | Description                                                                       |
| ------------------------------- | --------------------------------------------------------------------------------- |
| create\_branch\_single\_element | Creates an empty MMR, setting the provided element as the first one.              |
| create\_branch\_from\_message   | Utilizes a message from the Inbox, which is received from the L1MessagesSender.   |
| create\_branch\_from            | Creates a branch that detaches from an existing one, maintaining its root intact. |

#### **Tree growth methods**

`processBlocksBatch(bytes customEncodedParams)`

The reason behind having customEncodedParams is that in the previous versions, we used two methods:

```
function process_received_block(
	uint256 block_number,
	bytes header_rlp,
	bytes32[] mmr_peaks,
	uint256 mmr_size
)
```

AND

```
function process_batch(
	bytes[] headers_rlp,
	bytes32[] mmr_peaks,
	uint256 mmr_id,
	Nullable<uint256> reference_block,
	Nullable<uint256> mmr_index,
	Nullable<bytes32[]> mmr_proof,
)
```

The main reason behind having two methods is the need for differentiation between:

1. Processing an element already present in the MMR implies traversing the chain of headers from the left.
2. Processing a recent `blockhash` sent through the transport layer from L1, which is not present in the MMR.

### Facts Registry/Storage proofs verifier

This contract is responsible for verifying actual storage proofs and executing the final step of the workflow, which involves the verification of inclusion proofs.

The contract's main responsibilities are:

* Verify Merkle Mountain Range (MMR) inclusion proofs.
* Verify state/inclusion proofs related to accounts, receipts, transactions and/or proofs that attest to storage slot values.

This contract only reads data from the `HeadersStore`.

#### Notable Functions

| Function                                                                                                                                                                                                         | Description                                                                                                                                                                              |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| get\_account\_field(account: address, block: uint256, field: AccountField)                                                                                                                                       | Returns a cached, previously authenticated value of an account field.                                                                                                                    |
| get\_slot\_value(account: address, block: uint256, slot: bytes32)                                                                                                                                                | Retrieves a cached, previously authenticated storage slot value for a given account.                                                                                                     |
| get\_account(fields: AccountField\[], block\_header\_rlp: bytes, account: address, mpt\_proof: bytes, mmr\_index: uint256, mmr\_peaks: bytes32\[], mmr\_proof: bytes32\[], mmr\_id: uint256, mmr\_size: uint256) | Queries the HeadersStore with the provided parameters, ensures the provided header is valid, verifies the MPT proof, and returns the requested account fields.                           |
| get\_storage(block: uint256, account: address, slot: bytes32, mpt\_proof: bytes)                                                                                                                                 | Verifies that an MPT proof, attesting to a value of a storage slot, is valid by checking it against a previously cached storage hash of that account and returns the storage slot value. |

### **Future work**

#### **Batched verification**

Support for batch storage proof verification will enable up to 200 data entries to be proven per one generated proof, significantly decreasing costs.

Batched verification of storage proofs will provide a less latency-optimized alternative to using the FactsRegistry.

#### Facts Registry

Facts Registries support for `transaction_receipts` and `transactions`.

#### **Compressed Facts Registry**

A parallel version of FactsRegistry that is fully merkelized, similar to the HeadersStore. The only difference is that it would be paged to optimize for time-series queries.

## Current Deployments

Our current smart contract deployments can be found [here](/herodotus-docs/developers/contract-addresses.md).


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.herodotus.dev/herodotus-docs/protocol-design/architecture/smart-contracts.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
