Engines as contracts

A brief wip idea here. The motivation for this post is twofold: on one hand, these days, Anoma’s engine architecture is glossed over; other priorities, I know. On the other hand, I’ve been exploring related ideas in the Ethereum ecosystem, and projects like Near’s Shade Agents are interesting. For example, while these AI agents are not precisely Anoma engines, their framework’s architecture is a useful source of inspiration for implementing intent-solving engines, for example. Also, given how mature the Ethereum stack already is, it seems plausible to experiment with implementing Anoma-style engines there.

Let’s try to sketch a modular, registry-based architecture for deploying and managing “Engines” in the Ethereum ecosystem.

First, what is an Engine? We define “engines” as the system’s fundamental computational units in the Anoma Protocol specification. These are entities based on the Actor Model and consist of

  • Internal state (mutable)
  • Configuration (inmutable)
  • Message interfaces (inmutable)
  • Behaviours (guarded actions) (immutable)

The specification then enumerates several engines required for Anoma to operate, such as the Mempool worker engine and the Ordering shard engines. This way, to run Anoma following the specs, we must deploy multiple engines—possibly of the same kind (same specification, same job)—running in different network locations, yet all registered within the system. That registry is fixed, in principle. However, as the protocol evolves, new engine types can be added, the number of instances can grow to incorporate new functionalities, and we then bump the protocol version.

Now, how do we translate this architecture into an existing system like Ethereum? We can achieve the same Anoma engine-based architecture via several contracts implementing a modular, registry-based system composed of four key parts:

  1. The Registry. The central spine of the system. This contract acts as both a factory and a directory. It stores “Types” (blueprints or engine specifications) and tracks “Instances” (the actual deployed engines).

  2. The Engine Type. What is an engine type in this context? It is a contract acting as an abstract shell, implemented as an interface that describes the Anoam engine components. Crucially, these contracts do not contain business logic itself. Instead, an engine contract holds state (such as owner and status) and delegates logic to specialised, separate contracts known as Behaviours. This approach is similar in spirit to the Diamond Pattern EIP-2535, allowing for modularity and upgradability.

  3. The Behaviours. These are the actual brains of the engine. Behaviours are separate contracts that contain the engine’s logic. Since there is too much freedom in what an engine might need to do, we categorise behaviours based on their gas-consumption impact:

  • Isolated Behaviours (On-chain): These are logics that run in a context-aware manner entirely on-chain. The context is provided by the associated engine’s internal state. This is ideal for inexpensive computations.
  • Trusted Off-chain Behaviours (ZK): Here, we compute off-chain and push the result back on-chain using a Zero-Knowledge Proof. While this introduces more latency—as @cwgoes has pointed out—it is an attractive trade-off because it enables privacy-preserving execution and complex verification without bloating the chain.
  1. Clones (The Scale)
    With the engine type specified and behaviours separated, we can deploy instances. To efficiently support multiple instances of the same engine type, we use Clones (Proxy Contracts).

By leveraging EIP-1167 (Minimal Proxy Contract), we can deploy cheap copies of the engine contracts. These proxies utilise the same logic code (the implementation) but maintain their own custom internal state. Effectively, the proxy responds to every call by saying, “I don’t have any logic for the behaviours of this engine type; just forward this call to the actual implementation of my engine type.”

This architecture allows us to replicate the robust, actor-based model of Anoma within the EVM, but it does not account for gas costs, the practical impact of running off-chain computations via zk proofs, a fault-tolerant design, error handling in case of a crash, events of interest, and who knows what else.

A diagram of how this could look is as follows: note that the behaviours are black boxes for external contracts as well.

graph TD
    subgraph Registry["EngineRegistry"]
        Types["Types (blueprints)"]
        Instances["Instances (clones)"]
        Behaviours["Behaviours"]
    end

    Type1["EngineType A"] --> Types
    Type2["EngineType B"] --> Types

    Types -->|"clone()"| Instance1["Instance 1"]
    Types -->|"clone()"| Instance2["Instance 2"]
    Types -->|"clone()"| Instance3["Instance 3"]

    Instance1 --> Instances
    Instance2 --> Instances
    Instance3 --> Instances

    Behaviour1["Behaviour X"] --> Behaviours
    Behaviour2["Behaviour Y"] --> Behaviours

    Instance1 -.->|"execute()"| Behaviour1
    Instance2 -.->|"execute()"| Behaviour1
    Instance3 -.->|"execute()"| Behaviour2

There is a Solidity draft implementation of the corresponding interfaces and contracts to support this architecture. If you are interested, send me a dm, happy to show you.

cc’ @apriori @cwgoes @graphomath @Michael

2 Likes