SL-Chain Specification

This document outlines the specifications and design details of the Semantic Layer chain (SL-Chain).

About Semantic Layer Chain

The Semantic Layer Chain is an application-specific sequencing layer designed by Semantic Layer Labs. It provides application-specific sequencing using Verifiable Sequencing Rules (VSRs), Verifiable Aggregation Rules (VARs), and the MTX transaction standard.

SL-Chain is modified and developed based on go-ethereum (geth) client v1.14.8. Initially, SL-Chain will use the Proof-of-Authority (PoA) consensus mechanism to enable rapid development and iteration. Eventually, SL-Chain will transition to a decentralized consensus model, such as Proof-of-Stake (PoS), to enhance network security and decentralization.

SL-Chain is still under active development, and its design may undergo significant changes. Developers and contributors can refer to the SL-Chain codebase for the latest updates.

Design Principles

  • Avoid any unnecessary security trust for users

    The system is built and designed to not impose any additional risk or trust assumption on user operations. By using ERC-4337 standard, user operations within an MTX are authenticated by and executed in the ERC-4337 entry point contract. Other operations, such as dApp and solver operations, which are executed before and after user operations, are isolated from user operation execution. This reduces the need to trust Semantic Layer nodes and solvers, as isolated execution prevents them from performing unauthorized operations on behalf of the user.

  • Minimize hurdles for dApp integration and adoption

    The system is designed to minimize integration hurdles for dApps. dApps can start integrating with Semantic Layer by implementing frontend modifications, without the need to modify or deploy new smart contracts. Additionally, integrating with Semantic Layer does not change the security model of the dApps, as the integration imposes no additional smart contract risk to existing dApps and their users.

Consensus Mechanism: PoA

PoA: Clique

We use Clique as our Proof-of-Authority (PoA) consensus protocol. It’s developed and maintained by Ethereum foundation team and it’s designed to work with geth client with minimal modifications.

In future phases, SL-Chain will transition to more decentralized consensus model, such as Proof-of-Stake (PoS), to enhance network security and decentralization.

Network Overview

This document provides an overview of the SL-Chain, covering key concepts, network architecture, and example workflows. For more details please refer to the specifications and design documents for each component.

Overview

The SL-Chain enables application-specific sequencing with Verifiable Sequencing Rules (VSRs), Verifiable Aggregation Rules (VARs), and the MTX transaction standard.

MTX

MTX is a transaction standard developed by Semantic Layer Labs that works with the SL-Chain to enforce Verifiable Sequencing Rules (VSR) and Verifiable Aggregation Rules (VAR), providing enhanced control for dApps over transaction execution. MTX leverages the existing ERC-4337 standard, maximizing compatibility with current DeFi infrastructure. In the future, MTX can be extended to support additional intent standards, such as ERC-7521, to expand its functionality.

There are two types of MTX: UserMTX and SolverMTX.

  • UserMTX: It includes ERC-4337 User Operations signed by the user, along with dApp operations, VSR, and VAR defined by dApp developers.
  • SolverMTX: It contains solver operations for a given UserMTX.

See here for more detailed information about MTX.

VAR and VSR

Developers can deploy customized VSR and VAR on the SL-Chain as smart contracts. VSR and VAR contracts implement specific interfaces and functions, which the SL-Chain references to process the MTX with the defined VSR and VAR.

VSR exposes a view function that the SL-Chain can use to calculate the VSR score of an MTX bundle. The SolverMTX with the highest score will be selected for the given UserMTX and eventually executed in the execution environment.

VAR exposes functions that the SL-Chain can reference for aggregating multiple MTX bundles.

See here for more detailed information about VSR and VAR.

Architecture

The network mainly consists of the following components:

  • SL Nodes: A peer-to-peer (P2P) node network, with each node connecting to a set of solvers. It acts as a gateway network for SL-Chain, enabling solvers to efficiently access UserMTX and submit SolverMTX.

    Solvers submit their SolverMTX to connected nodes. SolverMTXs are simulated and evaluated based on the defined VSR/VAR. For each UserMTX, the node selects the best SolverMTX and submits it to the SL-Chain. Different nodes may connect to different solver sets, leading to multiple local-best MTX bundles for the same UserMTX. The SL-Chain processes these local-best bundles and selects the global best for execution.

    For MTX that does not require solver solutions, it will be submitted directly to the SL-Chain and processed.

  • SL Execution Contract: An entry point contract deployed on the destination execution environment (for example, Ethereum and L2s). MTX executor will call this execution contract to execute the bundled operations.

  • SL-Chain: A modified EVM with a set of stateful precompiles designed to facilitate MTX processing.

    After receiving MTX from SL nodes, the SL-Chain evaluates the MTX based on the specified VSR/VAR and selects the best MTX bundle. The MTX executor then sends the selected MTX bundle to the SL execution contract deployed on the destination execution environment for executing the bundled operations.

  • SL Oracle: The SL-Chain needs on-chain states from the destination execution environment to confirm the successful execution of the MTX. The SL oracle enables the SL-Chain to access blockchain and smart contract states on the destination execution environment.

SL-Chain High Level Workflow

The following is a high-level workflow of the SL-Chain, covering the process from UserMTX generation to SolverMTX selection and final on-chain submission. It provides a basic overview of how the Semantic Layer operates.

For more detailed explanations, please refer to the relevant sections for each component.

Semantic Layer Network Overview Semantic Layer Network Overview
  1. UserMTX Generation and Signing at the dApp Frontend: A user interacts with a dApp frontend, which generates ERC-4337 UserOperations based on the user's intent, and the ERC-4337 UserOperation is added to the EIP-712 object. Optionally, VSR address, VAR address, dApp operations, and other configurations can also be added to the EIP-712 object to create a UserMTX for the user to sign. Note that VAR and VSR are deployed by developers ahead of the time, and the UserMTX only includes the contract addresses of VAR and VSR instead of the code.

  2. Submitting UserMTX to SL Nodes: The signed UserMTX is submitted by the dApp frontend to the SL nodes through the SL node API endpoint. The SL nodes will broadcast UserMTX to the entire network of connected solvers.

  3. SolverMTX Submission: Each SL node connects with different sets of solvers. Solvers submit SolverMTXs containing the solver operations for the given UserMTX through their connected SL nodes.

  4. SolverMTX Evaluation and Selection:

    SolverMTXs go through two stages of evaluation and selection based on the VSR score defined in the UserMTX.

    • Local Best SolverMTX Selection: Each SL node evaluates and selects the SolverMTX with the best VSR score among those it has received. The SL node submits this local best SolverMTX to SL-Chain for further processing.
    • Global Best SolverMTX Selection: The SL-Chain receives the local best SolverMTXs from different nodes, with MTX processing precompiled contracts, the SL-Chain selects the global best SolverMTX (SolverMTX with the best VSR score) among the local best submissions.
  5. On-Chain Submission: The MTX executor packages the global best SolverMTX with the corresponding UserMTX and submits it to the SL execution contract deployed on the destination execution environment using the builder precompile.

MTX

MTX is a transaction standard developed by Semantic Layer Labs that works with SL-Chain to enforce Verifiable Sequencing Rules (VSR) and Verifiable Aggregation Rules (VAR), enabling enhanced control for dApps over transaction execution.

There are two types of MTX: UserMTX and SolverMTX.

  • UserMTX: It includes ERC-4337 User Operations signed by the user, along with dApp operations, VSR, and VAR defined by dApp developers.
  • SolverMTX: It contains solver operations for a given UserMTX.

There are three types of operations: user operations, dApp operations, and solver operations. User and dApp operations are defined in the UserMTX, generated at the dApp frontend, while solver operations are defined in the SolverMTX, submitted by solvers.

  • User Operations: These are ERC-4337 user operations that describe a user's interaction with the dApp, such as swapping on a DEX.
  • dApp Operations: These are operations the dApp wants to execute before and after user and solver operations, such as redistributing MEV revenue back to users or implementing security circuit breakers.
  • Solver Operations: These are operations the solver wants to execute before and after user operations, such as back-running or liquidation.

UserMTX

A UserMTX is the core transaction structure initiated by dApps on behalf of users and processed by Semantic Layer. It includes ERC-4337 User Operations signed by the user, dApp operations, along with specific configurations and VSR and VAR defined by the dApp.

/**
 * @notice UserMTX struct
 * Contains UserOperations, dApp operations, and configuration for VSR and VAR rules.
 */
struct UserMTX {
    PackedUserOperation[] userOperations; // Array of ERC4337 UserOperations to execute.
    address from; // The EOA address signing the UserMTX.
    uint256 nonce; // Unique value used by `from` to prevent replay attacks.
    address sponsor; // Address of the sponsor covering dApp operation costs; leave empty if no sponsor.
    uint256 maxSponsorship; // Maximum gas fee the `sponsor` will cover.
    uint8 noSOs; // a bit map restricts before and after solver operations. `00`:no restriction. `01` no after solver operations. `10`:no before solver operations. `11`: no solver operations.
    VSRStruct VSR; // Verifiable Sequencing Rule struct; orders transactions by solver bid amount if non-empty.
    VARStruct VAR; // Verifiable Aggregation Rule struct; no aggregation occurs if empty.
    DAppOP dappOP; // Struct for dApp operations executed before and after UserOperations and solver operations.
    bytes signature; // UserMTX signature signed by `from`.
}

A UserMTX contains the following fields:

UserMTX.userOperations

This field defines an array of ERC-4337 user operations, which are generated at dApp frontend based on user intents.

// ERC-4337 User Operations
struct PackedUserOperation {
    address sender;
    uint256 nonce;
    bytes initCode;
    bytes callData;
    bytes32 accountGasLimits;
    uint256 preVerificationGas;
    bytes32 gasFees;
    bytes paymasterAndData;
    bytes signature;
}

ERC-4337 account abstraction standard is used for the following reasons:

  • Easier adoption: ERC-4337 is a battle-tested and widely accepted standards. Semantic Layer can easily integrate with existing infrastructure and dApps that support ERC-4337 standard.
  • Minimal trust requirement: By using ERC-4337 standard, user operations within an MTX are authenticated by and executed in the ERC-4337 entry point contract. Other operations, such as dApp and solver operations, which are executed before and after user operations, are isolated from user operation execution. This reduces the need to trust Semantic Layer nodes and solvers, as isolated execution prevents them from performing unauthorized operations on behalf of the user.

User operations are eventually passed to the ERC-4337 entry point contract for final execution, with gas costs paid by users. dApps can also implement the ERC-4337 paymaster to sponsor these gas costs.

UserMTX.from

This field specifies an Externally Owned Account (EOA) address. This address signs the UserMTX and is responsible for covering the gas costs of executing the dApp operations.

UserMTX.nonce

This field contains the nonce for UserMTX.from, which is checked by the SL execution contract to prevent replay attacks. It also allows users to cancel or batch-cancel unsettled MTXs.

UserMTX.sponsor

This field specifies the sponsor address for covering gas costs associated with executing dApp operations. Leave it empty if no sponsorship is applied.

The SL execution contract includes a deposit manager to manage gas costs for dApp operations. dApp developers can deposit funds to cover gas costs for their own dApp operations.

When UserMTX.sponsor is specified, the SL execution contract will deduct gas costs from the sponsor's account. If the sponsor cannot fully cover the costs, UserMTX.from will pay the remaining amount.

See here for detailed information on gas compensations.

UserMTX.maxSponsorship

This field specifies the maximum amount the UserMTX.sponsor is willing to contribute to cover dApp operational costs. Leave it empty if no sponsorship is applied.

UserMTX.noSOs

This field is a bitmap set by the dApp to indicate whether solver operations are restricted.

  • 00: Default state. Solvers can perform both before and after operations.
  • 01: Restricts after solver operation.
  • 10: Restricts before solver operation.
  • 11: Disables all solver operations. UserMTX will be executed directly without SolverMTX.

UserMTX.VSR

This field provides the Verifiable Sequencing Rule (VSR) contract information for the UserMTX.

Developers can deploy a VSR as a smart contract on the SL-Chain. The contract exposes a view function that returns the VSR score based on the solver's bid amount and other relevant data.

SolverMTXs for a given UserMTX are processed and selected on the SL-Chain based on the VSR score, with the SolverMTX that produces the highest score being added to the MTX bundle with the UserMTX. The MTX bundle will then be executed in the destination execution environment.

If this field is not provided, SolverMTXs are selected based on the solver bid amount by default, with the highest bid being chosen.

See here for detailed notes for Verifiable Sequencing Rule (VSR).

interface IVSR {
    /**
     *
     * @param bid solver bid amount
     * @param data other data
     */
    function score(uint256 bid, bytes calldata data) external view returns (uint256);
}

struct VSRStruct {
    IVSR vsrContract;
    bytes data;
}

UserMTX.VAR

This field provides the Verifiable Aggregation Rule (VAR) contract information for the UserMTX.

Developers can deploy VAR as a smart contract on the SL-Chain. The VAR contract exposes functions for the SL-Chain to aggregate MTXs.

See here for detailed notes for Verifiable Aggregation Rule (VAR).

interface IVAR {
    /**
     * @dev It returns the UserMTXs queueing for processing.
     * @param id the id of the queue. MTX needs to be bundled together can be added to queue with the same id.
     */
    function mtxQueue(uint256 id) external view returns (UserMTX[] memory);

    /**
     * @dev It signals whether a mtx queue is ready for aggregation.
     * @param id the id of the queue. MTX needs to be bundled together can be added to queue with the same id.
     */
    function canAggregate(uint256 id) external view returns (bool);

    /**
     * @dev Call this function to process the MTXs queueing for processing.
     * @param id the id of the queue. MTX needs to be bundled together can be added to queue with the same id.
     * @param batchSize the number of the mtx to aggregated
     */
    function aggregateCurrentQueue(uint256 id, uint256 batchSize) external;
}

struct VARStruct {
    IVAR varContract;
}

UserMTX.dappOP

This field defines the dApp operations, which are actions the dApp intends to execute before and after user and solver operations. The dApp can specify which operations to execute using a bitmap format.

The isAuthorized() in the dApp operation contract checks if the specified address (UserMTX.from) is authorized to use this dApp operation contract in the UserMTX.

To minimize the hurdles for dApp integration with Semantic Layer, the dApp operation contracts are optional. If provided:

  • the solver bid payment will be sent to the bidReceiver defined in the dApp operation.

If not provided:

  • the solver bid payment will be sent to the UserMTX.from address, which is the address authorizing UserMTX.userOperations and the entire UserMTX.
/**
 * @notice DAppOperation struct
 * Contains data and hooks for dApp operations, with a bitmap specifying which hooks should be executed.
 */
struct DAppOP {
    IDAppHook dAppHook; // dApp operation contract.
    uint8 hookBitmap; // Bitmap to indicate which hooks to call; 1 = call, 0 = skip.
        // Bitmap format: 1010 (bit 3: preSO, bit 2: preUO, bit 1: postUO, bit 0: postSO).
    bytes preSOHookData; // Data required for the pre solver operation; leave empty if not applicable.
    bytes preUOHookData; // Data required for the pre user operation; leave empty if not applicable.
    bytes postUOHookData; // Data required for the post user operation; leave empty if not applicable.
    bytes postSOHookData; // Data required for the post solver operation; leave empty if not applicable.
}

/**
 * @notice interface for dapp operation contract
 */
interface IDAppHook {
    /**
     * @notice Checks if the specified address (UserMTX.from) is authorized to use this dApp operation contract in the UserMTX
     * @param user The address to check for authorization.
     * @return A boolean indicating if the address is authorized (true) or not (false).
     */
    function isAuthorized(address user) external view returns (bool);

    /**
     * @notice Returns the address designated to receive the Solver's bid.
     * @param data Additional data required for determining the receiver; leave empty if not applicable.
     * @return The address that should receive the bid.
     */
    function bidReceiver(bytes memory data) external view returns (address);

    /**
     * execution order for all operations
     * 1. DApp Operation: pre-solver operations
     * 2. Solver Operation: pre-user operations
     * 3. DApp Operation: pre-user operations
     * 4. User operations
     * 5. DApp Operation: post-user operations
     * 6. Solver Operation: post-user operations
     * 7. DApp Operation: post-solver operations
     *
     */

    /**
     * @notice dApp operation hook to execute before solver operations.
     * @dev Should only be called by the SL execution contract.
     * @param hookData Data needed for the hook function.
     */
    function preSolverOperationHook(bytes memory hookData) external;

    /**
     * @notice dApp operation hook to execute before user operations.
     * @dev Should only be called by the SL execution contract.
     * @param hookData Data needed for the hook function.
     */
    function preUserOperationHook(bytes memory hookData) external;

    /**
     * @notice dApp operation hook to execute after user operations.
     * @dev Should only be called by the SL execution contract.
     * @param hookData Data needed for the hook function.
     */
    function postUserOperationHook(bytes memory hookData) external;

    /**
     * @notice dApp operation hook to execute after solver operations.
     * @dev Should only be called by the SL execution contract.
     * @param hookData Data needed for the hook function.
     */
    function postSolverOperationHook(bytes memory hookData) external;
}

UserMTX.signature

This field contains the UserMTX signature signed by UserMTX.from.

SolverMTX

A SolverMTX is the core transaction structure initiated by solvers. It includes the bid amount the solver is willing to offer and the operations they intend to execute before and after user operations.

/**
 * @notice SolverMTX struct
 * Contains Solver operations linked to a specific UserMTX.
 */
struct SolverMTX {
    address from; // The solver's EOA address signing the SolverMTX.
    uint256 nonce; // Unique value used by `from` to prevent replay attacks.
    bytes32 userMTXHash; // Hash representing the associated UserMTX for verification.
    SolverOP solverOP; // Struct for solver operations executed before and after UserOperations.
    bytes signature; // SolverMTX signature signed by `from`.
}

A SolverMTX contains the following fields:

SolverMTX.from

This field specifies an Externally Owned Account (EOA) address, which signs the SolverMTX and covers the gas costs for executing solver operations.

The SL execution contract includes a deposit manager to handle gas costs for dApp and solver operations. Solvers can deposit funds in advance or sign a permit allowing the contract to transfer funds from their addresses to cover gas costs for solver operations.

SolverMTX.nonce

This field contains the nonce for SolverMTX.from, which is checked by the SL execution contract to prevent replay attacks. It also allows solvers to cancel or batch-cancel unsettled MTXs.

SolverMTX.userMTXHash

This field contains a unique hash that identifies the corresponding UserMTX within Semantic Layer. The solver includes this hash to indicate the UserMTX they intend to solve.

SolverMTX.solverOP

This field contains the SolverOP object. It contains the bid amount the solver is willing to offer and solver operations that the solver intends to execute before and after user operations. Solver can specify which operations to execute using a bitmap format.

The solver operation contract must be deployed by the solver in advance. The payBid() function will be called by SL execution contract to enforce the solver bid payment.

/**
 * @notice SolverOP struct
 * Contains data for solver operations, including bid amount and hooks for pre- and post-operation.
 */
struct SolverOP {
    uint256 bidAmount; // Amount bid by the solver is willing to offer
    ISolverHook solverHook; // Contract for solver operation hooks.
    uint8 hookBitmap; // Bitmap to indicate which hooks to call; 1 = call, 0 = skip.
        // Bitmap format: 10 (bit 1: preUO, bit 0: postUO).
    bytes preUOHookData; // Data required for the pre user operation; leave empty if not applicable.
    bytes postUOHookData; // Data required for the  post user operation; leave empty if not applicable.
}

/**
 * @notice Interface for solver operation contract.
 */
interface ISolverHook {
    /**
     * @notice Checks if the specified address (SolverMTX.from) is authorized to use this solver operation contract in the SolverMTX
     * @param user The address to check for authorization.
     * @return A boolean indicating if the address is authorized (true) or not (false).
     */
    function isAuthorized(address user) external view returns (bool);

    /**
     * execution order for all operations
     * 1. DApp Operation: pre-solver operations
     * 2. Solver Operation: pre-user operations
     * 3. DApp Operation: pre-user operations
     * 4. User operations
     * 5. DApp Operation: post-user operations
     * 6. Solver Operation: post-user operations
     * 7. DApp Operation: post-solver operations
     *
     */

    /**
     * @notice Executes before the user operation.
     * @dev Should only be called by the SL execution contract.
     * @param hookData Data required for the pre-user operation.
     */
    function preUserOperation(bytes memory hookData) external;

    /**
     * @notice Executes after the user operation.
     * @dev Should only be called by the SL execution contract.
     * @param hookData Data required for the post-user operation.
     */
    function postUserOperation(bytes memory hookData) external;

    /**
     * @notice pay the specified bid amount to the given address.
     * @dev Should only be called by the SL execution contract.
     * @param to Address to receive the bid amount.
     * @param value bid Amount to be paid.
     */
    function payBid(address to, uint256 value) external;
}

SolverMTX.signature

This field contains the SolverMTX signature signed by SolverMTX.from.

The life cycle of an MTX

Starting from being initiated on the dApp front end, to eventual execution on the destination execution environment, an MTX will go through the following 5 stages. Developers (e.g., dApp developers and solvers) can use the Semantic Layer SDK to initialize the MTX object.

Stage 0 - frontend packs UserOperation Array

At this stage, the dApp frontend generates a set of user operations based on user intents and interactions. After obtaining the user's signature, the dApp frontend packs these operations into an array, which is then included in the UserMTX.

Stage 1 - frontend adds VSR, VAR, and dApp operations

At this stage, the dApp frontend adds the VSR and VAR contract information for the UserMTX. Optionally, the dApp frontend can also add dApp operations, which are operations to be executed before and after user and solver operations, to the UserMTX.

Stage 2 - user signs the UserMTX

The user must sign the final UserMTX before it is sent to Semantic Layer.

The SL execution contract verifies that the signer is UserMTX.from and checks the nonce to prevent tampering or spoofing. The signed UserMTX is then sent to SL nodes via an API endpoint.

MTX Life Cycle #1 MTX Life Cycle #1

Stage 3 - Semantic Layer nodes process the MTX

Once a Semantic Layer node receives the UserMTX, it broadcasts the UserMTX to the entire network and begins processing it. Each UserMTX has an auction window during which solvers can submit SolverMTXs to the Semantic Layer nodes.

During the auction window, each Semantic Layer node runs the VSR score function on received SolverMTXs and keeps track of the SolverMTX with the highest score. After the auction window expires, the node submits the local best MTX bundle—containing the SolverMTX with the highest score seen by that node—to the SL-Chain.

If no SolverMTX is submitted to the Semantic Layer node, and the auction window passes, the Semantic Layer node will submit the UserMTX to Semantic Layer chain with a null SolverMTX and a score of 0.

Some UserMTXs may set the bitmap noSOs to have more granular restrictions on solver operations. We can explicitly set whether we allow solvers to do front-running, back-running, or sandwiching by setting the noSOs bitmap. These UserMTXs will be submitted to the Semantic Layer chain directly as soon as the Semantic Layer node receives such a UserMTX. For UserMTXs that do not allow any solver operations, this type of UserMTX does not have a VSR specified for them. We expect this type of UserMTX to be either time-sensitive or contain no MEV revenue to be extracted. These UserMTXs will be executed directly without waiting for a SolverMTX.

Each Semantic Layer node might be connected with different sets of solvers, therefore, for a given UserMTX(n), there might be multiple local best MTX bundles submitted to Semantic Layer chain, each being submitted by different Semantic Layer nodes with locally observed most optimal SolverMTX.

Stage 4 - Semantic Layer nodes pack the final MTX

At each block of the SL-Chain, the leader Semantic Layer node uses the VSR specified in the UserMTX to select the globally observed most optimal SolverMTX. This SolverMTX is added to the UserMTX to construct the final PackedMTX, which is serialized and executed on the Semantic Layer execution contract deployed on the destination execution environment.

The final MTX executed on the SL execution contract is a packed MTX containing the UserMTX, the selected SolverMTX, and the VSR/VAR data.

/**
 * @notice PackedMTX struct
 * Represents the final MTX bundle to be executed in the SL execution contract.
 */
struct PackedMTX {
    address payable beneficiary; // Address to receive gas compensation, typically the caller submitting the PackedMTX for on-chain execution.
    UserMTX userMTX; // Contains user transaction details in the UserMTX struct.
    SolverMTX solverMTX; // Contains SolverMTX details; leave empty if no SolverMTX is included.
    VSRInfo vsrInfo; // Execution details for the Verifiable Sequencing Rule (VSR); leave empty if VSR is not applied.
    VARInfo varInfo; // Execution details for the Verifiable Aggregation Rule (VAR); leave empty if VAR is not applied.
}

The packed MTX contains the following fields:

  • PackedMTX.beneficiary This field specifies the address that receives gas compensation for on-chain execution. The MTX executor submitting the final PackedMTX for on-chain execution on the destination execution environment must cover upfront transaction costs. The SL execution contract calculates the gas used for involved operations, deducts the costs from the accounts responsible for the respective gas costs, and transfers these funds to the PackedMTX.beneficiary address. See here for more detailed information on gas compensations.
  • PackedMTX.userMTX This field specifies the UserMTX object that will be executed on the destination chain.
  • PackedMTX.solverMTX This field specifies the SolverMTX object that will be executed on the destination chain.
  • PackedMTX.vsrInfo This field specifies the execution details for VSR, such as the VSR score and the block number when the VSR score is calculated. This field is purely for on-chain record purposes and can be omitted if not necessary.
  • PackedMTX.varInfo This field specifies the execution details for VAR, such as the block number when the VAR aggregation bundle is constructed. This field is purely for on-chain record purposes and can be omitted if not necessary.

Stage 5 - on-chain execution

The final packed MTX is executed by the SL execution contract on the destination chain, which verifies the MTX through several key steps:

  • Verifies the ERC-4337 UserOperation Array: This field checks if all ERC-4337 operations in the array are from the same user address.
  • Verifies the UserMTX Signer and Signature: This field checks if the signer matches the from field in the UserMTX, verifies the signer nonce, and checks if the signer is authorized to use the specified dApp operation contract.
  • Verifies the Sponsorship in UserMTX: This field checks if the sponsor allows the from address to use its funds to cover dApp operation gas costs.
  • Verifies the SolverMTX Signer and Signature: This field checks if the signer matches the from field in the SolverMTX, verifies the signer nonce, and checks if the signer is authorized to use the specified solver operation contract.

The ERC-4337 UserOperations are forwarded to the ERC-4337 entry point contract for user operation execution. The dApp and solver operations are executed directly by the SL execution contract.

User, dApp, and solver operations are executed in the following order:

	/**
   * execution order for all operations
   * 1. DApp Operation: pre-solver operations
   * 2. Solver Operation: pre-user operations
   * 3. DApp Operation: pre-user operations
   * 4. User Operations
   * 5. DApp Operation: post-user operations
   * 6. Solver Operation: post-user operations
   * 7. DApp Operation: post-solver operations
   */

If the specified solver bid payment in SolverMTX is not zero, the SL execution contract enforces the solver bid payment by calling the payBid() function in the solver operation contract. It transfers the bid payment to the SL execution contract first to verify that the received amount matches the specified bid amount. Then, the SL execution contract transfers the payment to the bid receiver address.

MTX Life Cycle #2 MTX Life Cycle #2

VSR and VAR

Overview

Verifiable Sequencing Rule (VSR) and Verifiable Aggregation Rule (VAR) can be deployed as smart contracts on SL-Chain to enable application-specific sequencing.

Verifiable Sequencing Rule (VSR)

Sequencing rules can be deployed by developers as smart contracts, exposing a read-only (view or pure) function that Semantic Layer node uses to compute the VSR score for an MTX.

VSR allows sequencing based on the solver bid amount as well as any other custom criteria defined by the dApp. The SolverMTX with the highest VSR score will be selected for a given UserMTX.

DApp developers can define sequencing rules as VSR to achieve application-specific sequencing. For example, dApp developers can define the score function to return the amount of solver MEV payment. This VSR incentivizes solvers to compete with each other to maximize the amount of MEV payment made to the protocol treasury, thus achieving MEV internalization for the protocol. VSR as a smart contract can provide maximal flexibility to dApp developers, and we expect developers to leverage that flexibility and create more sophisticated sequencing rule to build novel applications.

The VSR.score() function contains two fields: bid and data.

  • bid: The bid amount specified in SolverMTX. When calculating the VSR score, the SL node takes the bid amount number from the SolverMTX as the bid argument value.
  • data: Any other necessary data required, is packed into UserMTX when generated by the dApp frontend.
interface IVSR {
    /**
     *
     * @param bid solver bid amount
     * @param data other data
     */
    function score(uint256 bid, bytes calldata data) external view returns (uint256);
}

struct VSRStruct {
    IVSR vsrContract;
    bytes data; // for the `data` argument of the IVSR.score() function
}

Example: VSR considers solver bid amount only

If the data field is empty, the VSR score is based solely on the solver bid amount. When calculating the VSR score, the SL node takes the bid amount from the SolverMTX and inputs it into the VSR.score() function.

If the data field is not empty, the VSR score is then calculated with the logic defined in the VSR.score() function. See below for an example.

Example: VSR for a DEX

In this example, a VSR contract is defined for a Uniswap-like decentralized exchange (DEX). The VSR score calculation considers factors beyond just the solver bid amount. The user's swap information is packed into the VSR data field. The solver operation that yields the best swap outcome for the user receives the highest VSR score, and solvers must compete with each other to yield the best swap outcome. For example, a solver can add liquidity to the DEX to improve the user's swap result.

// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.6.6;

import "../../interfaces/IVSR.sol";
import "@uniswap/v2-periphery/contracts/libraries/UniswapV2Library.sol";
/**
 * @title VSR (Verifiable Sequencing Rule contract)
 * @dev This simple VSR example calculates goal score based the amount out when user swap.
 */

contract VSR is IVSR {
    address immutable univ2Factory;

    constructor(address factory_) public {
        univ2Factory = factory_;
    }

    function score(uint256 bid, bytes calldata data) external view override returns (uint256) {
        (uint256 amountIn, address[] memory path) = abi.decode(data, (uint256, address[]));
        uint256[] memory amounts = UniswapV2Library.getAmountsOut(univ2Factory, amountIn, path);
        uint256 len = amounts.length;
        return amounts[len - 1];
    }
}

In this example, the SL node simulates executing all user dApp and solver operations to evaluate their impact. The VSR.score() function is then called based on this simulation.

When the VSR.score() function is called, the solver bid amount from the SolverMTX is used for the bid argument, and the data field from the VSRStruct in the UserMTX is used for the data argument.

Verifiable Aggregation Rule (VAR)

DApp developers can create their aggregation rules using VAR. With VAR, MTXs can be bundled and executed together based on the customized rules defined by dApps.

The VAR contract must implement the IVAR interface. MTXs that can be aggregated are stored under the same queue ID.

  • The canAggregate() function indicates whether the current MTX queue is ready for aggregation. dApp developers writes evaluation logics in this function, which evaluates to true once the MTXs in the queue are ready for aggregation and execution.
  • The aggregateCurrentQueue() function is called by SL nodes to aggregate the MTXs in the queue. The batch size defines the maximum number of MTXs aggregated per operation, with any remaining MTXs processed in subsequent operations. The SL-Chain processes these MTX bundles and batch executes them in the destination execution environment.

Note: VAR support is still under active development, and its design may undergo significant changes. Developers and contributors can refer to the SL-Chain codebase for the latest updates.

interface IVAR {
    /**
     * @dev It returns the UserMTXs queueing for processing.
     * @param id the id of the queue. MTX needs to be bundled together can be added to queue with the same id.
     */
    function mtxQueue(uint256 id) external view returns (UserMTX[] memory);

    /**
     * @dev It signals whether a mtx queue is ready for aggregation.
     * @param id the id of the queue. MTX needs to be bundled together can be added to queue with the same id.
     */
    function canAggregate(uint256 id) external view returns (bool);

    /**
     * @dev Call this function to process the MTXs queueing for processing.
     * @param id the id of the queue. MTX needs to be bundled together can be added to queue with the same id.
     * @param batchSize the number of the mtx to aggregated
     */
    function aggregateCurrentQueue(uint256 id, uint256 batchSize) external;
}

Gas Compensation

Overview

MTX is processed on SL-Chain and executed in the destination execution environment. SL-Chain is the application-specific sequencing layer for dApps that processes MTX based on the Verifiable Sequencing Rules and Verifiable Aggregation Rules. The processed MTX is executed in the destination execution environment where the dApp is deployed.

Processing MTX on SL-Chain consumes computational and storage resources, and submitting MTX to SL-Chain will incur gas costs.

  • Testnet Stage: To cover SL-Chain costs, the Semantic Layer provides a faucet allowing users and dApps to obtain free gas tokens.
  • Mainnet Stage: TBD, with the possibility to use Semantic Layer token as a gas token.

The following sections focus on gas costs incurred on the destination execution environment.

Types of Gas Costs

Gas costs on the destination chain are categorized as follows:

  1. ERC-4337 User Operations: Costs for executing user operations.
  2. dApp Operations: Costs for executing dApp operations, if provided in the UserMTX.
  3. Solver Operations: Costs for executing solver operations.

The MTX executor submitting the final PackedMTX for on-chain execution on the destination execution environment must cover the upfront transaction costs. The SL execution contract calculates the gas used for each operation, deducts the costs from the responsible accounts, and transfers the funds to the PackedMTX.beneficiary address (usually the MTX executor address) to reimburse its gas costs.

Since user operations are managed by the ERC-4337 contract, gas costs for these operations are handled by the ERC-4337 entry point contracts. The SL node provides the PackedMTX.beneficiary as the ERC-4337 beneficiary address when sending user operations for execution, allowing the SL node to receive gas reimbursement from the ERC-4337 contract.

Calculating Gas Costs

Gas costs for each operation are calculated by comparing gasleft() before and after the operation is executed.

    uint256 preGas = gasleft();

    // some operations

    uint256 afterGas = gasleft();
    uint256 gasUsed = preGas - afterGas;

SL Deposit Manager

The SL execution contract includes an SL deposit manager to handle gas reimbursement for dApp and solver operations.

dApp developers and solvers can deposit funds into this contract in advance to cover gas costs for their own dApp and solver operations. Note for dApp operations, it is also optional to let user pay for dApp operations, as dApp operations are nested within UserMTX. See section “Gas Costs for dApp Operations” for details.

In addition to depositing in advance, dApp developers and solvers can sign a permit authorizing the SL execution contract to transfer funds from their EOA address to cover gas costs on demand.

Gas costs for dApp and solver operations are calculated separately. The SL execution contract deducts funds from deposited balances, and the total deducted amount is sent to the PackedMTX.beneficiary address.

Gas Costs for ERC-4337 User Operations

Semantic Layer supports ERC-4337 and uses the ERC-4337 entry point contract for executing user operations. Therefore, gas estimation and costs are handled by the ERC-4337 contract. User operations are sent to the ERC-4337 contract with the beneficiary address set to PackedMTX.beneficiary, and the ERC-4337 contract sends the gas compensation to this address.

Gas costs are deducted from the user's ERC-4337 smart contract wallet. If dApps wish to sponsor these costs, dApp developers can implement an ERC-4337 paymaster contract. Gas costs are first deducted from the paymaster, and any remaining costs are deducted from the user's wallet if the paymaster does not cover the full amount.

Gas Costs for dApp Operations

The UserMTX.from address is responsible for covering gas costs for dApp operations by default. Under default, the user pays for dApp operations.

dApps can also sponsor dApp operations. A sponsor and maximum sponsorship amount can be specified in the UserMTX. If sponsorship is specified, the SL execution contract verifies whether the sponsor allows sponsorship for the UserMTX.from address. If allowed, gas costs are first deducted from the sponsor's balance in the SL deposit manager, and any remaining funds are deducted from UserMTX.from if sponsorship does not fully cover the gas costs.

Gas Costs for Solver Operations

The SolverMTX.from address is responsible for covering gas costs for solver operations.

Solver operations often involve complex executions and are typically for the solver's own benefit only, thus solver operations cannot be sponsored. The solver operations costs must be fully covered by the SolverMTX.from address. However, if dApps want to incentivize solvers, dApps can include a payment to the solver as part of its dApp operations.

SL Network Architecture

Overview

Each SL node is connected to:

  1. MTX endpoint server (centralized service)
    1. for fetching UserMTX
    2. alternatively, SL node can expose its API endpoint to receive UserMTX directly
  2. a list of solvers
    1. for sharing UserMTX and receiving SolverMTX
  3. other SL nodes
    1. for establishing consensus on the state of the SL-Chain
    2. for broadcasting UserMTX and SolverMTX bundle with other SL nodes

Each SL node runs two clients:

  1. SL-Chain client (built based on Geth)
    1. for establishing the consensus on the state of the SL-Chain
  2. SL-node client (built using libp2p in Golang with TCP as the transport protocol)
    1. for retrieving UserMTX from the centralized MTX endpoint server
    2. for sharing UserMTX and receiving SolverMTX from solvers
    3. for broadcasting UserMTX and SolverMTX bundle with other SL nodes
Semantic Layer Network Architecture Semantic Layer Network Architecture

Benefits of separated clients

SL-Chain client is built based on Geth with the addition of Semantic Layer stateful precompiles SL-EVM, and the SL-Chain client handles tasks where a global consensus is required, such as selecting and executing the globally most optimal MTX bundle. Tasks where a global consensus is not required, such as selecting the locally most optimal MTX bundle, is handled by SL-node client. Separating the consensus-dependent tasks from consensus-independent tasks provides following benefits:

  1. Reduced Communication Delay: SL nodes are geographically distributed and each has a local view of connected solver and SolverMTXs. Directly connecting SL nodes to solvers with the SL-node client reduces communication delays, allowing solvers to quickly receive information and submit their SolverMTXs.
  2. Load Balancing: Each SL node connects to a limited set of solvers. The SL-node P2P network acts as a load balancer to distribute computes efficiently and prevent overloading any single node.
  3. Reduce Execution Load on SL-Chain: SL nodes perform simulations and preprocessing to reduce the load on SL-Chain. SL-node client will execute VSR locally on all SolverMTXs submitted to the node, and only locally most optimal SolverMTX will be submitted to SL-Chain.

Note that Verifiable Sequencing Rules and Verifiable Aggregation Rules are deployed to SL-Chain, and the correct execution of the VSR and VAR are cryptographically secured by SL-Chain. SL-Chain is initially secured by Proof of Authority (PoA) consensus mechanism and will later transition to Proof of Stake (PoS) consensus mechanism.

Network Configuration

Node Functionality

Each SL node is connected to multiple types of entities. SL-Chain client and SL-node client executes the following tasks:

  • UserMTX Retrieval (SL-node client)
    • SL node will retrieve UserMTX from a centralized MTX endpoint server, which exposes an API-endpoint for users to submit signed UserMTX from the front-end
    • Note that a centralized MTX endpoint server is not required for Semantic Layer network. SL node can also expose an API-endpoint for users to submit signed UserMTX from the front-end directly to the SL node
  • Provide UserMTX to solvers (SL-node client)
    • Solvers can retrieve UserMTX from connected SL nodes
  • Retrieve SolverMTX from solver (SL-node client)
    • Solvers submit SolverMTX to connected SL nodes
    • Note that each solver might be connected and submitting SolverMTX to multiple SL nodes to mitigate potential packet loss
  • Construct local best MTX bundle (SL-node client)
    • SL-node client will execute received SolverMTX with the VSR specified for the UserMTX
    • A VSR score is generated for each SolverMTX
    • The SolverMTX with the highest VSR score is regarded as the locally observed most optimal SolverMTX. This SolverMTX is combined with the UserMTX to construct a MTX bundle
  • Broadcast UserMTX and SolverMTX bundle (SL-node client)
    • This MTX bundle is broadcasted to other SL nodes
  • Reach a consensus on SL-Chain state (SL-Chain client)
    • Pack MTX bundles into SL-Chain block
      • SL-Chain block proposer adds the MTX bundles to a SL-Chain block
    • Select a MTX executor for the bundle
      • A SL-node is selected as the MTX executor
    • Execute Verifiable Sequencing Rule to select the globally most-optimal MTX bundle
      • SL-EVM executes the VSR to find globally most-optimal MTX bundle
    • Execute Verifiable Aggregation Rule to aggregate MTX bundles
      • SL-EVM executes the VAR to aggregate MTX bundles
  • Serialize and execute MTX bundle on the destination execution environment (SL-node client)
    • MTX executor (a SL node) serializes the MTX bundle and submits to the block builder or sequencer of the destination execution environment

Transport Protocols

TCP is used as the transport protocol for communication between nodes. UDP is solely used for peer discovery for its simplicity.

Peer Discovery Mechanism

The SL node network leverages a hybrid bootstrap node and DHT-based peer discovery mechanism for optimized connectivity and resilience. Bootstrap nodes serve as initial entry points, allowing SL nodes to quickly discover active peers when joining the network. Once connected, each node utilizes the DHT to dynamically expand and maintain its peer connections across the network, ensuring decentralized discovery.

By combining the stability of bootstrap nodes with the adaptability of the DHT, this approach minimizes reliance on any single entry point, enabling seamless peer discovery even if multiple bootstrap nodes go offline. This robust setup enhances both connectivity and network resilience.

Communication and Encryption

  • Data Encryption: UserMTX and SolverMTX will be encrypted using ECIES (Elliptic Curve Integrated Encryption Scheme) with asymmetric encryption using public and private key pairs
  • Secure and Encrypted Communication Channel: The client uses libp2p-noise protocol with the XX handshake pattern for secure and encrypted communication channels. The XX handshake pattern provides mutual authentication between nodes, ensuring a secure channel before any data exchange occurs.

SL-EVM

Built on top of the go-ethereum client, SL-Chain is EVM compatible. Both Verifiable Sequencing Rule and Verifiable Aggregation Rule can be written in solidity and deployed to SL-Chain. Additional stateful precompiles have been added to SL-EVM, which offers more expressive functionalities for the VAR and VSR contracts.

Stateful Precompiles

SL-Chain includes a set of stateful precompile contracts designed to streamline MTX processing and extend the functionality of VARs and VSRs. Compared to the standard, stateless precompiles in geth, SL-Chain adds support for stateful precompiles, which enable more expressive functionalities beyond traditional stateless operations.

All stateful precompiles implement the following interface. The Run method is called when the precompile contract is invoked. A portion of EVM states such as stateDB are exposed and can be accessed by the precompile contract.

// StatefulPrecompiledContract defines the interface for the precompile contracts with state.
//
// It replaces the old stateless `PrecompiledContract` interface.
// All precompile contracts now need to follow this interface.
type StatefulPrecompiledContract interface {
	RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use

	// Run runs the precompiled contract.
	Run(accessibleState AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, err error)
}

A precompile contract contains different functions. Those functions implements the following interfaces. The run method is called when the precompile function is being executed and the requiredGas method returns the gas cost to execute the function.

// StatefulPrecompileFunction defines a function implemented by a stateful precompile
type StatefulPrecompileFunction struct {
	// selector is the 4 byte function selector for this function
	selector []byte
	// run is performed when this function is selected
	run RunStatefulPrecompileFunc
	// requiredGas returns the gas cost for executing the function
	requiredGas RequiredGasPrecompileFunc
}

// StatefulPrecompileFallbackfunction defines the fallback function implemented by a stateful precompile
type StatefulPrecompileFallbackfunction struct {
	// run is performed when this function is selected
	run RunStatefulPrecompileFunc
	// requiredGas returns the gas cost for executing the function
	requiredGas RequiredGasPrecompileFunc
}
  • Example: ReadOwner.sol

    Here is an example of the stateful precompile. The ReadOwner precompile can read the owner address from another contract.

    /*
    This file implements a ReadOwner precompile.
    
    It reads the `owner` from another Ownable contract at storage slot 0x00....0.
    
    interface ReadOwner {
        function readOwner(address ownableContract) external  returns(address owner);
    }
    
    the example ownable contract we read from
    
    contract Ownable {
        address public owner  = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;
    }
    */
    
    package vm
    
    import (
    	"fmt"
    
    	"github.com/ethereum/go-ethereum/accounts/abi"
    	"github.com/ethereum/go-ethereum/common"
    	"github.com/ethereum/go-ethereum/sl/artifacts"
    )
    
    var (
    	// Ownable precompile contract address
    	readOwnerContractAddress = common.HexToAddress("0x0300000000000000000000000000000000000002")
    )
    
    // readOwner implements function readOwner(address ownableContract) external view returns(address owner);
    //
    // It reads the owner variable in a contract
    func readOwner(accessibleState AccessibleState, caller common.Address, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, err error) {
    	// stateDB := accessibleState.GetStateDB()
    	ownableContractAddr, err := unpackReadOwnerInput(input)
    	if err != nil {
    		return nil, err
    	}
    	if ownableContractAddr == (common.Address{}) {
    		return nil, fmt.Errorf("empty ownable contract")
    	}
    
    	// read from ownable contract
    	owner, err := getOwnerFromOwnable(accessibleState, ownableContractAddr)
    	if err != nil {
    		return nil, err
    	}
    	// pack output
    	packedOutput, err := packReadOwnerOutput(owner)
    	if err != nil {
    		return nil, err
    	}
    	return packedOutput, nil
    
    }
    
    // readOwnerRequiredGas implements RequiredGas.
    func readOwnerRequiredGas(input []byte) uint64 {
    	return uint64(2000)
    }
    
    // unpackReadOwnerInput unpacks the input of readOwner function.
    func unpackReadOwnerInput(input []byte) (common.Address, error) {
    	res, err := artifacts.ContractABI.Methods["readOwner"].Inputs.Unpack(input)
    	if err != nil {
    		return common.Address{}, err
    	}
    
    	unpacked := *abi.ConvertType(res[0], new(common.Address)).(*common.Address)
    	return unpacked, nil
    }
    
    // packReadOwnerOutput packs the output of readOwner function.
    func packReadOwnerOutput(owner common.Address) ([]byte, error) {
    	return artifacts.ContractABI.Methods["readOwner"].Outputs.Pack(owner)
    }
    
    type MyContractRef struct {
    	address common.Address
    }
    
    func (c *MyContractRef) Address() common.Address {
    	return c.address
    }
    
    // getOwnerFromOwnable reads the owner in ownableContract at storage slot 0x0.
    func getOwnerFromOwnable(accessibleState AccessibleState, ownableContractAddr common.Address) (common.Address, error) {
    	// perform a staticcall
    	calldata, err := artifacts.ContractABI.Pack("owner")
    	if err != nil {
    		return common.Address{}, nil
    	}
    
    	output, _, err := accessibleState.StaticCall(&MyContractRef{address: readOwnerContractAddress}, ownableContractAddr, calldata, 10000)
    	if err != nil {
    		return common.Address{}, err
    	}
    
    	var res common.Address
    	err = artifacts.ContractABI.UnpackIntoInterface(&res, "owner", output)
    	if err != nil {
    		return common.Address{}, err
    	}
    	return res, nil
    }
    
    // createReadOwnerPrecompile creates the readOwner precompile contract.
    func createReadOwnerPrecompile() StatefulPrecompiledContract {
    	var functions []*StatefulPrecompileFunction
    	abiFunctionMap := map[string]PrecompileFuncs{
    		"readOwner": {RunFunc: readOwner, GasFunc: readOwnerRequiredGas},
    	}
    
    	for name, precompileFunc := range abiFunctionMap {
    		method := artifacts.ContractABI.Methods[name]
    		functions = append(functions, NewStatefulPrecompileFunction(method.ID, precompileFunc.RunFunc, precompileFunc.GasFunc))
    	}
    
    	statefulContract, err := NewStatefulPrecompileContract(StatefulPrecompileFallbackfunction{}, functions)
    	if err != nil {
    		panic(err)
    	}
    
    	return statefulContract
    }
    
  • MTX Precompile

    The MTX Precompile contains logic for handling computationally intensive operations during MTX processing, providing necessary performance and security optimizations.

    MTXPrecompile contract address is defined as 0x0300000000000000000000000000000000000003.

    The following methods are currently implemented:

    • MTX Selection: Based on VSR and VAR scores, along with solver reputation (if enabled), the MTX Precompile determines the most optimal MTX bundle for execution.
    • MTX Encryption: Sensitive data, such as user operations, solver solutions, and strategies are encrypted to maintain confidentiality until execution. This ensures that critical information is protected against unauthorized access.
    • MTX Decryption: Provides utility functions that developers can call to reveal encrypted data once the MTX has been fully settled.
    • UserMTX Partial Decryption: Provides a utility function allowing users and solvers to partially reveal the content of the UserMTX while keeping signatures confidential, before the UserMTX is fully settled.
    interface IMTXPrecompile { 
        /**
         * @dev return the solver mtx with the highest vsr score
         * @param mtxHubAddr mtxHub contract address
         * @param usermtxHash usermtx hash
         * @return solvermtxHash selected solvermtx hash with the highest vsr score
         * @return score vsr score
         */
        function selectSolverMTXByVSR(address mtxHubAddr, bytes32 usermtxHash)
            external
            view
            returns (bytes32 solvermtxHash, uint256 score);
    
        
        /**
         * @dev it decrypts a solvermtx. It will revert if the mtx is not settled yet.
         * @param mtxHubAddr mtxHub contract address
         * @param solvermtxHash solvermtx hash
         */
        function decryptSolverMTX(address mtxHubAddr, bytes32 solvermtxHash)
            external
            view
            returns (SolverMTX memory solvermtx);
    
        
        /**
         * @dev it decrypts a usrmtx. It will revert if the mtx is not settled yet.
         * @param mtxHubAddr mtxHub contract address
         * @param usermtxHash usermtx hash
         */
        function decryptUserMTX(address mtxHubAddr, bytes32 usermtxHash) external view returns (UserMTX memory solvermtx);
    
        /**
         * @notice helper functions that can be used to get the encrypted mtx.
         * @dev encryption includes randomness. Use this function in read-only function or off-chain only.
         * @param solvermtx solvermtx object to be encrypted
         */
        function encryptSolverMTX(SolverMTX calldata solvermtx) external view returns (bytes memory encrypted);
    
        /**
         * @notice helper functions that can be used to get the encrypted mtx.
         * @dev encryption includes randomness. Use this function in read-only function or off-chain only.
         * @param usermtx usermtx object to be encrypted
         */
        function encryptUserMTX(UserMTX calldata usermtx) external view returns (bytes memory encrypted);
    
        /**
         * @dev partially reveal a usermtx content without any sigantures.
         * @param mtxHubAddr mtxHub contract address
         * @param usermtxHash usermtx hash
         */
        function partialDecryptUserMTX(address mtxHubAddr, bytes32 usermtxHash) external view returns (UserMTX memory solvermtx);
    }
    
    
  • Builder Precompile

    The Builder Precompile offers functions for submitting MTXs as a bundled transaction to the builder of the destination execution environment, such as an Ethereum builder. This bundle contains the transaction required to execute MTXs on the SL execution contract of the destination chain.

    Builder Precompile contract address is defined as: 0x0b0000000000000000000000000000000000000b.

    • Create Bundle: Allows developers to create a bundle of MTXs for submission.
    • Submit Bundle: Allows developers to submit the created bundle to the block builder or sequencer.
    interface BuilderPrecompile {
        function createBundle(bytes memory data) external view returns(bytes memory bundle);
        function submitBundle(string memory rpcURL, bytes memory data) external returns(bytes memory msg, bool ok);
    }
    

Security & Privacy

Security

Minimal trust requirement

By using ERC-4337 standard, user operations within an MTX are authenticated by and executed in the ERC-4337 entry point contract. Other operations, such as dApp and solver operations, which are executed before and after user operations, are isolated from user operation execution. This reduces the need to trust Semantic Layer nodes and solvers, as isolated execution prevents them from performing unauthorized operations on behalf of the user.

Privacy

To ensure integrity and fairness in MTX submission and processing, certain information must remain confidential until the MTX is fully settled, preventing unauthorized execution of user operations or replication of solver strategies.

To address these privacy requirements, MTXs are encrypted. Given the relatively small data size of MTXs, asymmetric encryption is preferred for its simplicity over hybrid encryption. MTX are encrypted using a designated public key rather than being transmitted in clear text. Additionally, the MTX precompile offers helper functions to simplify the encryption and decryption process.

Testnet Faucet

We self-host an open-sourced testnet faucet: eth-faucet

Block Explorer

We self-host open-sourced block explorer: Blockscout.