# EVM Script Factories for MEV Boost Allowed List
## Introduction
The current doc describes the implementation details of EasyTrack EVM factories to manage an instance of the MEV Boost Relay Allowed List contract. Currently, the RMC committee holds the rights to manage the List directly. The proposed change will grant EasyTrack's `EvmScriptExecutor` the rights to modify the Allowed List via the introduced EVM script factories, while the RMC will retain the right to initiate EasyTrack motions.
The high-view overview of the management flow is presented in the below diagram.

## Rights & Permissions
For the introduced EVM script factories to operate properly, it is required to set EasyTrack's `EvmScriptExecutor` as `manager` of the `MEVBoostRelayAllowedList` contract.
| Role | EVM Script Factory | Methods |
| --------- | --------------------------------------------- | --------------------------- |
| `manager` | [AddMEVBoostRelays](#addmevboostrelays) | `add_relay` |
| `manager` | [RemoveMEVBoostRelays](#removemevboostrelays) | `remove_relay` |
| `manager` | [EditMEVBoostRelays](#editmevboostrelays) | `remove_relay`, `add_relay` |
Address can be set by calling the `setManager` method of the `MEVBoostRelayAllowedList` contract.
```solidity!
MEVBoostRelayAllowedList.set_manager(evmScriptExecutorAddress)
```
This method can be called only by the `owner` of the `MEVBoostRelayAllowedList`, which is a Lido Agent for the currently deployed instance.
## `IMEVBoostRelayAllowedList` Interface
```solidty!
// SPDX-FileCopyrightText: 2025 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.6;
/// @title Lido's MEV Boost Relay Allowed List interface
interface IMEVBoostRelayAllowedList {
struct Relay {
string uri;
string operator;
bool is_mandatory;
string description;
}
// View Functions
function get_relays_amount() external view returns (uint256);
function get_relay_by_uri(string memory relay_uri) external view returns (Relay memory);
// State-Changing Functions
function add_relay(
string memory uri,
string memory operator,
bool is_mandatory,
string memory description
) external;
function remove_relay(string memory uri) external;
}
```
## Trade-offs
Transferring the `manager` role to the `EvmScriptExecutor` introduces a new constraint on Allowed List modifications. **Direct modifications to the Allowed List through the `MEVBoostRelayAllowedList` contract will not be possible and all changes will be subject to a delay becasue of a mandatory objection period**.
The current implementation of the EVM script factories **does not support including multiple actions of different types in a single motion**. For example, it is not possible to simultaneously add a relay and remove another one in one motion. To achieve the same results a separate motion must be created for each type of action performed on the Allowed List to be run in parallel.
Each factory validates the script against the state of the Allowed List at the time of script creation and again before execution upon motion enactment. **If multiple motions modifying the same relays in conflicting ways are proposed in close succession, they may pass initial creation validation but fail at enactment**. For example, if one motion is created to remove a relay and another to edit the same relay, both will initially validate successfully since the relay URI is still present in the list at the moment of their creation. However, if the removal motion is enacted first, the edit motion will fail at execution because the relay no longer exists in the list. This limitation should be carefully considered when creating motions to avoid unintended failures.
## AddMEVBoostRelays
- **inherits**: [TrustedCaller](https://github.com/lidofinance/easy-track/blob/master/specification.md#trustedcaller)
- **implements**: [IEVMScriptFactory](https://github.com/lidofinance/easy-track/blob/master/specification.md#ievmscriptfactory)
Allows creation of EVM script to add a set of MEV Boost Relays operators to the instance of the [`MEVBoostRelayAllowedList`](https://github.com/lidofinance/mev-boost-relay-allowed-list/blob/f3f84cdc9e93704c0b74ac00b172d4a6c83949a5/contracts/MEVBoostRelayAllowedList.vy).
### Data Types
No additional data types are required, using the struct from the Allowed List interface:
```solidity!
interface IMEVBoostRelayAllowedList {
struct Relay {
string uri;
string operator;
bool is_mandatory;
string description;
}
}
```
### Immutable Variables
- `IMEVBoostRelayAllowedList mevBoostRelayAllowedList` - address of the instance of the `MEVBoostRelayAllowedList`
### Methods
#### createEVMScript(address,bytes)
- **Arguments**:
- `address _creator` - the creator of the EVM Script
- `bytes memory _evmScriptCallData` - an ABI encoded array `(IMEVBoostRelayAllowedList.Relay[] memory decodedCallData)`
- **Visibility**: `external`
- **Mutability**: `view`
- **Modifiers**: [onlyTrustedCaller(\_creator)](https://github.com/lidofinance/easy-track/blob/master/specification.md#storage-variables-3)
- **Returns**: `(bytes memory)`
Creates an EVM script to add a list of relays to the instance of the [`MEVBoostRelayAllowedList`](https://github.com/lidofinance/mev-boost-relay-allowed-list/blob/f3f84cdc9e93704c0b74ac00b172d4a6c83949a5/contracts/MEVBoostRelayAllowedList.vy).
This function will be called by the EasyTrack when the motion is created and then again when it is enacted.
To successfully create and enact motion, the following onchain requirements must be met:
- Input data **MUST** not be empty
- Input data **MUST** be decoded successfully into an array of `IMEVBoostRelayAllowedList.Relay`
- The number of relays added **MUST BE** greater than 0
- The total number of relays in the mevBoostRelayAllowedList instance, after adding the new ones, **MUST NOT** exceed `MAX_NUM_RELAYS`
- Relays uris **MUST NOT** be empty
- Relays uris and string parameters `operator` and `description` **MUST NOT** exceed the string length limit of 1024 characters
- Relays uris **MUST NOT** have duplicates
- Relays uris **MUST NOT** be already presented in the allow list contract.
#### decodeEVMScriptCallData(bytes)
- **Arguments**:
- `bytes memory _evmScriptCallData` - an ABI encoded array `(IMEVBoostRelayAllowedList.Relay[])`
- **Visibility**: `external`
- **Mutability**: `pure`
- **Returns**: `(IMEVBoostRelayAllowedList.Relay[] memory)`
Decodes `_evmScriptCallData` into array `(IMEVBoostRelayAllowedList.Relay[] relays)`.
## RemoveMEVBoostRelays
- **inherits**: [TrustedCaller](https://github.com/lidofinance/easy-track/blob/master/specification.md#trustedcaller)
- **implements**: [IEVMScriptFactory](https://github.com/lidofinance/easy-track/blob/master/specification.md#ievmscriptfactory)
Allows creation of EVM script to remove a set of MEV Boost Relays operators on the instance of the [`MEVBoostRelayAllowedList`](https://github.com/lidofinance/mev-boost-relay-allowed-list/blob/f3f84cdc9e93704c0b74ac00b172d4a6c83949a5/contracts/MEVBoostRelayAllowedList.vy).
### Data Types
As relays are identified by their URIs, the input data for each relay to be removed is simply a URI string, so no additional data types are required.
### Immutable Variables
- `IMEVBoostRelayAllowedList mevBoostRelayAllowedList` - address of the instance of the `MEVBoostRelayAllowedList`
### Methods
#### createEVMScript(address,bytes)
- **Arguments**:
- `address _creator` - the creator of the EVM Script
- `bytes memory _evmScriptCallData` - an ABI encoded array `(string[] memory decodedCallData)`
- **Visibility**: `external`
- **Mutability**: `view`
- **Modifiers**: [onlyTrustedCaller(\_creator)](https://github.com/lidofinance/easy-track/blob/master/specification.md#storage-variables-3)
- **Returns**: `(bytes memory)`
Creates an EVM script to remove a list of relays from the instance of the `MEVBoostRelayAllowedList`.
This function will be called by the EasyTrack when the motion is created and then again when it is enacted.
To successfully create and enact motion, the following onchain requirements must be met:
- Input data **MUST** not be empty
- Input data **MUST** be decoded successfully into an array of `string`
- The number of relays removed **MUST BE** more than 0
- Relays uris **MUST NOT** be empty
- Relays uris **MUST NOT** have duplicates
- Relays uris **MUST** be already presented in the allow list contract.
#### decodeEVMScriptCallData(bytes)
- **Arguments**:
- `bytes memory _evmScriptCallData` - an ABI encoded array `(string[])`
- **Visibility**: `external`
- **Mutability**: `pure`
- **Returns**: `(string[] memory)`
Decodes `_evmScriptCallData` into array `(string[] relayUris)`.
## EditMEVBoostRelays
- **inherits**: [TrustedCaller](https://github.com/lidofinance/easy-track/blob/master/specification.md#trustedcaller)
- **implements**: [IEVMScriptFactory](https://github.com/lidofinance/easy-track/blob/master/specification.md#ievmscriptfactory)
Allows creation of EVM script to edit a set of MEV Boost Relays operators on the instance of the [`MEVBoostRelayAllowedList`](https://github.com/lidofinance/mev-boost-relay-allowed-list/blob/f3f84cdc9e93704c0b74ac00b172d4a6c83949a5/contracts/MEVBoostRelayAllowedList.vy).
Any existing relay can be edited by its URI, and the new data for the relay is provided in the input data.
### Data Types
No additional data types are required, using the struct from the Allowed List interface:
```solidity!
interface IMEVBoostRelayAllowedList {
struct Relay {
string uri;
string operator;
bool is_mandatory;
string description;
}
}
```
### Immutable Variables
- `IMEVBoostRelayAllowedList mevBoostRelayAllowedList` - address of the instance of the `MEVBoostRelayAllowedList`
### Methods
#### createEVMScript(address,bytes)
- **Arguments**:
- `address _creator` - the creator of the EVM Script
- `bytes memory _evmScriptCallData` - an ABI encoded array `(IMEVBoostRelayAllowedList.Relay[] memory decodedCallData)`
- **Visibility**: `external`
- **Mutability**: `view`
- **Modifiers**: [onlyTrustedCaller(\_creator)](https://github.com/lidofinance/easy-track/blob/master/specification.md#storage-variables-3)
- **Returns**: `(bytes memory)`
Creates an EVM script to edit a list of relays on the instance of the [`MEVBoostRelayAllowedList`](https://github.com/lidofinance/mev-boost-relay-allowed-list/blob/f3f84cdc9e93704c0b74ac00b172d4a6c83949a5/contracts/MEVBoostRelayAllowedList.vy).
This function will be called by the EasyTrack when the motion is created and then again when it is enacted.
Every portion of the data can be modified, except for URI which acts as a primary key.
> MEV Boost Relay Allowed List contract does not have a method to directly edit a relay, so the edit operation is implemented as an ordered combination of `remove_relay` and `add_relay` operations in a single EVM script. The relay is removed as a first action, and then added with the new data. To ensure idempotency, same URI is used to create the relay as was used when removing it, which means that it's impossible to edit the URI (primary key) this way.
To successfully create and enact motion, the following onchain requirements must be met:
- Input data **MUST** not be empty
- Input data **MUST** be decoded successfully into an array of `IMEVBoostRelayAllowedList.Relay`
- The number of relays edited **MUST BE** more than 0
- Relays uris **MUST NOT** be empty
- Relays uris and string parameters `operator` and `description` **MUST NOT** exceed the string length limit of 1024 characters
- Relays uris **MUST NOT** have duplicates
- Relays uris **MUST** be already presented in the allow list contract.
#### decodeEVMScriptCallData(bytes)
- **Arguments**:
- `bytes memory _evmScriptCallData` - an ABI encoded array `(IMEVBoostRelayAllowedList.Relay[])`
- **Visibility**: `external`
- **Mutability**: `pure`
- **Returns**: `(IMEVBoostRelayAllowedList.Relay[] memory)`
Decodes `_evmScriptCallData` into array `(IMEVBoostRelayAllowedList.Relay[] relays)`.
## MEVBoostRelaysInputUtils Library
A library that encapsulates input validation logic and calldata decoding. Used by the EVM script factories internally to reduce code duplication.
### Methods
#### validateRelays(Relay[],Relay[],bool)
- **Arguments**:
- `IMEVBoostRelayAllowedList.Relay[] memory _relays` - Array of Relay structs to validate
- `IMEVBoostRelayAllowedList.Relay[] memory _currentAllowedRelays` - Current list of allowed relays from the MEVBoostRelayAllowedList contract
- `bool _expectExistence` - Switches the expected existence of the relay URIs in the current relay list
- if true, the relay URIs should exist in the current relay list and will revert otherwise
- if false, the relay URIs should NOT exist in the current relay list and will revert otherwise
- **Visibility**: `internal`
- **Mutability**: `pure`
- **Returns**: `(bool)`
Performs validation on an array of relay structs. Validates the relay parameters (URI, operator, and description), checks for duplicate input, and verifies the existence of relay URIs in the current relay list based on the `_expectExistence` flag.
#### validateRelayURIs(string[],Relay[])
- **Arguments**:
- `string[] memory _relayURIs` - Array of Relay URI strings to validate
- `IMEVBoostRelayAllowedList.Relay[] memory _currentAllowedRelays` - Current list of allowed relays from the MEVBoostRelayAllowedList contract
- **Visibility**: `internal`
- **Mutability**: `pure`
- **Returns**: `(bool)`
Performs validation on an array of relay URIs. Validates the URI length and presence, checks for duplicate input, and verifies the existence of relay URIs in the current relay list.
#### decodeCallDataWithRelayStructs(bytes)
- **Arguments**:
- `bytes memory _evmScriptCallData` - Encoded relay struct array
- **Visibility**: `internal`
- **Mutability**: `pure`
- **Returns**: `(IMEVBoostRelayAllowedList.Relay[] memory)`
Decodes the call data for a relay struct array.
#### decodeCallDataWithRelayURIs(bytes)
- **Arguments**:
- `bytes memory _evmScriptCallData` - Encoded relay URI string array
- **Visibility**: `internal`
- **Mutability**: `pure`
- **Returns**: `(string[] memory)`
Decodes the call data for a relay URI string array.
## References
- [GitHub Pull Request](https://github.com/lidofinance/easy-track/pull/81) with the new factories