---
eip: 8349
title: Index-Based Multi-Facet Proxy
description: A modular proxy architecture that uses compact facet indices for protocol routing while preserving function selectors for compatibility.
author: Artem Buchikhin (@Arhemius)
discussions-to: https://ethereum-magicians.org/t/erc-8349-index-based-multi-facet-proxy/29054
status: Draft
type: Standards Track
category: ERC
created: 2026-07-25
requires: 165
---

## Abstract

This standard introduces Cento, an index-based multi-facet routing model
for the Ethereum Virtual Machine.

Unlike existing modular proxy standards, this specification establishes an
explicit facet identity, realized as routing indices appended to calldata,
while preserving conventional function selectors for compatibility with
standardized Ethereum interfaces and external tooling.

The introduction of an explicit facet identity enables facet-level routing
granularity with significantly reduced routing metadata, eliminates selector
management overhead, and avoids selector collision risks while maintaining
full compatibility with existing Ethereum infrastructure, protocols, wallets,
explorers, and development tools.

The specification additionally defines how interoperable implementations of
the Cento Proxy architecture organize calldata routing, atomic upgrades,
facet management, introspection, and security.

## Motivation

The Ethereum ecosystem has demonstrated strong demand for modular smart
contract architectures. [ERC-2535](./eip-2535.md) introduced selector-centric
modular routing (Diamond), enabling protocols to distribute logic across
multiple facets while maintaining a single external address. This approach has
proven valuable in production deployments and established modular proxies as a
practical foundation for complex upgradeable protocols.

Being an alternative design point for multi-facet protocols, the same
motivations of ERC-2535 apply to this standard.

In multi-facet routing models, routing ultimately resolves to selecting a
target facet for execution based on routing information contained in calldata.
Existing selector-centric routing models infer this facet identity from
behavioral identifiers, conflating routing and behavioral concerns.

- Function selectors exist to provide compatibility between independently
developed software, including wallets, block explorers, SDKs, standards, and
external smart contracts
- Facet identities exist solely to determine which facet executes protocol
logic

As protocols become larger and more modular, using function selectors as
routing identifiers causes routing metadata to scale with exported protocol
functions rather than facets. Consequently, evolution of this routing
metadata becomes increasingly coupled to selector management.

This architectural coupling gives rise to several
recurring engineering challenges:

1. **Routing metadata scales with function count**: As protocols evolve,
selector tables grow proportionally with externally callable functions rather
than module count. Every upgrade requires selector management regardless of
whether protocol organization changes.

2. **Selector collision concerns**: Protocol developers must ensure that
independently developed facets do not unintentionally expose identical function
selectors. While selector collisions are uncommon, avoiding them remains an
ongoing consideration during protocol composition, library development, and
integration.

3. **Misaligned abstractions**: Function selectors identify externally
observable behavior, whereas facets represent implementation modules. Coupling
these distinct concerns constrains routing to operate at the granularity of
functions rather than protocol modules.

4. **Upgrade complexity**: Multi-facet upgrades require modifying routing
metadata for potentially hundreds of individual selectors even when the
protocol evolves at the level of facets.

### Architectural Innovation

Because the target facet is the fundamental routing destination in multi-facet
routing, its identity should be represented explicitly rather than inferred
from behavioral identifiers.

This standard approaches modular routing from a different architectural
perspective. Instead of using function selectors as facet identifiers, it
introduces the **Cento routing model**, which realizes that principle by
representing facet identity as a routing index appended to calldata. This model
treats protocol routing and interface compatibility as independent concerns:

- **Protocol Routing** identifies target facets using routing indices appended
to the end of calldata.
- **Interface Compatibility** maps standardized Ethereum function selectors
to routing indices for Protocol Routing

This way, function selectors retain their original purpose as
**compatibility identifiers** for standardized Ethereum interfaces, while
routing indices become dedicated facet identifiers for Protocol Routing.

By separating routing and behavioral concerns in this way, routing metadata
naturally scales with facets rather than exported functions, enabling
facet-level routing granularity while preserving compatibility with existing
Ethereum standards and tooling.

## Specification

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and
"OPTIONAL" in this document are to be interpreted as described in
[RFC 2119](https://www.rfc-editor.org/rfc/rfc2119) and
[RFC 8174](https://www.rfc-editor.org/rfc/rfc8174).

### Terminology

The terminology defined below follows the conceptual hierarchy established
by this specification, beginning with foundational concepts, followed by
routing-model semantics, architectural organization, and Cento-specific
terminology.

#### Foundational Concepts

**Proxy**: A software design pattern that intercepts and forwards interactions
to another implementation while preserving a stable interface and storage
context.

**Routing Model**: A conceptual model defining how routing destinations are
identified within a proxy-based system. It establishes the permissible routing
semantics of a multi-facet proxy architecture, including the facet identity,
identifier collection, routing mechanisms, routing adapters, and upgrade
operation. Routing models are further characterized by their architectural
approach, facet identity type, routing model type, function routing types,
and routing granularity. A routing model does not prescribe architectural
organization and may be realized by multiple proxy architectures.

**Proxy Architecture**: An architectural pattern that organizes modular systems
around the Proxy design pattern. A proxy architecture defines the architectural
components, their responsibilities, and their interactions. It MAY directly
employ the Proxy design pattern or build upon a routing model.

**Multi-Facet Proxy Architecture**: A proxy architecture that organizes a
modular system into multiple implementation modules ("facets") and realizes
a routing model by selecting and constraining its permissible set of routing
semantics. It additionally defines its constituent components, routing
metadata, identifier collection location, and the upgrade, observability,
deployment, and storage layout models.

#### Routing Model Classification

**Architectural Approach**: The fundamental architectural perspective adopted by
a routing model for representing facet identity. This specification recognizes
selector-centric and facet-centric approaches.

**Facet Identity Type**: The manner in which a routing model represents facet
identity. Facet identity MAY be implicit or explicit.

**Routing Model Type**: The concrete realization of a routing model's facet
identity. Examples include selector-based and index-based routing models.

**Function Routing Types**: The categories of externally callable functions
defined by a routing model. Examples include Protocol Functions and
Compatibility Functions.

**Routing Granularity**: The architectural level at which routing resolves
execution. Function-level routing identifies individual externally callable
functions, whereas facet-level routing identifies implementation modules.

#### Routing Semantics

**Facet Identity**: The architectural identity assigned to a facet for routing
purposes. A routing model defines how facet identity is represented and
resolved. Examples include function selectors and routing indices. Any facet
identity MUST be derivable from some portion of calldata.

**Identifier Collection**: A routing data structure associating facet
identities with their corresponding facet implementations. Examples include
selector-to-facet mappings and routing tables.

**Routing Mechanism**: The procedure by which a routing model resolves a target
facet from its facet identity. Examples include Selector Routing (routing
mechanism implemented by Diamond) and Protocol Routing.

**Routing Adapter**: An optional compatibility layer translating one routing
representation into another before the routing mechanism resolves the target
facet. Interface Compatibility is an example of such an adapter.

**Upgrade Operation**: The operation defined by a routing model for atomically
modifying its identifier collection while maintaining routing metadata
consistency. Examples include [`diamondCut()`](../assets/eip-2535/reference/Diamond.sol)
and [`atomicUpdate()`](../assets/eip-8349/reference/interfaces/IFacetManager.sol).

#### Architectural Organization

**Architectural Component**: A contract fulfilling a distinct architectural
role within a multi-facet proxy architecture. Such components collectively
realize the architecture by implementing its routing, upgrade, observability,
deployment, storage layout, or other architectural models.

**Router**: An architectural component serving as the entrypoint of a
multi-facet proxy architecture. The router receives external calls, maintains
the shared storage context, resolves the destination facet according to the
routing model, and delegates execution.

**Facet**: An architectural component serving as an implementation module in a
multi-facet proxy architecture. Facets execute protocol logic within the
router's shared storage context.

**Routing Metadata**: The data describing a routing architecture. It consists
of an identifier collection and any additional auxiliary routing data. While
the identifier collection defines how the router dispatches calls, the
auxiliary routing data describes what that collection implies. Examples include
an occupancy bitmap, implied by the routing table to accelerate introspection,
and the set of supported interfaces, implied by the installed facets
represented by the routing table.

**Identifier Collection Location**: The architectural location where an
identifier collection is maintained. Examples include router storage, router
bytecode, and beacon storage.

**Upgrade Model**: The architectural organization exposing and governing a
routing model's upgrade operation. Examples include
[`FacetManager`](../assets/eip-8349/reference/facets/FacetManager.sol) and
[`DiamondCutFacet`](../assets/eip-2535/reference/Diamond.sol).

**Observability Model**: The architectural organization exposing routing state
and protocol composition for external inspection. Examples include
[`Observability`](../assets/eip-8349/reference/facets/Observability.sol) and
[`DiamondLoupeFacet`](../assets/eip-2535/reference/Diamond.sol).

**Deployment Model**: The architectural organization governing deployment and
instantiation of routers and facets. Examples include default deployment,
clone deployment, and parameterized clone deployment.

**Storage Layout Model**: The architectural organization governing how storage
is allocated to prevent collisions between implementation modules.

#### Cento Terminology

**Cento**: An index-based multi-facet routing model that identifies facets
using routing indices. Hereafter, Cento MAY also be referred to as the Cento
routing model. The model encompasses both Protocol Routing and Interface
Compatibility mechanisms, but only the former is mandatory for an
implementation to conform to the Cento routing model. The term **"Cento"**
MAY also serve as an architectural identifier for patterns, standards,
implementations, and implementation components based on this routing model.
A proxy implementing the Cento routing model MAY be
classified as a member of the **Cento Proxy** family.

**Cento Proxy**: A multi-facet proxy architecture realizing the Cento
routing model. This specification defines the Cento Proxy architecture by
constraining the permissible semantics of the Cento routing model and
specifying the architectural organization required for interoperable
implementations.

**Routing Index**: An explicit facet identifier represented by an unsigned
integer. This specification standardizes 8-bit routing indices (0–255)
appended to the end of calldata for Protocol Routing, which implementations
of this standard MUST conform to. Future specifications MAY support wider
routing indices while preserving interoperability.

**Routing Table**: A facet identifier collection associating routing indices
with active facet addresses.

**Protocol Routing**: A routing mechanism that resolves the destination facet
using a routing index appended to the end of calldata.

**Interface Compatibility**: A routing adapter mapping standardized Ethereum
function selectors to routing indices before Protocol Routing.

**Protocol Function**: A function exposed through Protocol Routing. Protocol
functions do not require globally unique function selectors.

**Compatibility Function**: A function additionally exposed through Interface
Compatibility to support standardized Ethereum interfaces.

**Atomic Update**: An upgrade operation that atomically modifies the routing
metadata by installing, replacing, or removing facets while maintaining
consistency between the facet identifier collection and its auxiliary routing
data. The term MAY refer to the architectural operation itself, its canonical
implementation (`atomicUpdate()`), or its corresponding event
([`AtomicUpdate`](../assets/eip-8349/reference/interfaces/IFacetManager.sol)),
depending on context.

### Routing Architecture

This specification realizes the Cento routing model through a Protocol Routing
mechanism and an Interface Compatibility routing adapter. Both paths resolve
calls to routing indices and share the same facet dispatch mechanism.

A routing architecture of this specification MUST maintain its identifier
collection in the router's storage, making the router the authoritative
owner of routing state.

#### Protocol Routing

Protocol Functions are invoked by appending a routing index (one byte) to
calldata.

```text
+------------------+---------------+
| Arbitrary Bytes  | Routing Index |
+------------------+---------------+
  (0 or more)       (1 byte)
```

The preceding bytes may be ABI-encoded function calls, raw data, or empty.

The router MUST:

1. Receive the call with an appended routing index
2. Extract the routing index from the final byte
3. Resolve the routing index and ensure that it identifies an active facet
4. Exclude the routing byte from delegated calldata
5. Delegate execution to the facet via `DELEGATECALL`

The router delegates all preceding calldata unchanged. The facet interprets
this calldata according to its own logic (standard ABI decoding, raw parsing,
or receive function handling if empty).

#### Compatibility Routing

Compatibility Functions are any standardized Ethereum interfaces exposed by
the protocol. In Compatibility Routing, they are dispatched through
the Interface Compatibility routing adapter:

```text
+----------+-----------+
| Selector | Function  |
|          | Arguments |
+----------+-----------+
(4 bytes)
```

The router MUST detect standardized interface selectors and dispatch the
corresponding Compatibility Functions through the Interface Compatibility
routing adapter without appending a routing index. Compatibility Routing
coexists with Protocol Routing without mutual interference.

Examples of standardized interfaces include [ERC-165](./eip-165.md) (interface
detection) and [ERC-173](./eip-173.md) (ownership), though any Ethereum
interface using function selectors SHOULD be supported through compatibility
routing.

#### Calldata Encoding

Protocol Routing appends a routing index to the end of arbitrary calldata:

```text
+------------------+---------------+
| Calldata (0+ B)  | Index (1 B)   |
+------------------+---------------+
```

The preceding calldata MAY consist of:

- ABI-encoded function calls: `function(args) + index`
- Raw data: `arbitrary bytes + index`
- No delegated calldata: `index byte only` (triggers receive function)

The router removes the routing index before delegation, preserving the
preceding calldata exactly as supplied.

**Examples:**

- `function(args) + index byte` → facet receives `function(args)`
- `raw data + index byte` → facet receives `raw data`  
- `index byte only` → facet receives empty calldata (triggers receive function)

Compatibility Functions are dispatched through Interface Compatibility routing
adapter and do not append a routing index.

#### Delegatecall Semantics

Protocol execution uses `DELEGATECALL`:

```solidity
delegatecall(gas(), facetAddress, _calldata, calldataSize, 0, 0)
```

The delegated facet executes within the router's shared storage context.
The router MUST forward all available gas to the delegated facet and MUST
propagate returndata, including reverts, unchanged to the caller.

#### Fallback Function Example Implementation

The following educational example demonstrates one implementation of the
routing architecture defined by this specification. An extensive reference
implementation is available in the
[Reference Implementation](#reference-implementation) section.

```solidity
pragma solidity ^0.8.29;

fallback() external payable {
    assembly {
        let cds := calldatasize()
        let idx := 0
        let stripLen := 0
            
        // Example dispatch heuristic:
        // odd  calldata -> Protocol Routing
        // even calldata -> Compatibility Routing
        if and(cds, 1) {
            idx := byte(0, calldataload(sub(cds, 1)))
            stripLen := 1
        } else {
            // Compatibility routing: dispatch by selector
            switch shr(224, calldataload(0))
            case 0x01ffc9a7 { idx := ERC165_INDEX }  // ERC-165
            case 0x8da5cb5b { idx := ERC173_INDEX }  // ERC-173: owner()
            case 0xf2fde38b { idx := ERC173_INDEX }  // ERC-173: transferOwnership()
            // Non-uniform calldata size may also be supported
            default { idx := byte(0, calldataload(sub(cds, 1))) }
        }
            
        // Unified dispatch
        let facet := sload(add(STORAGE_SLOT, idx))
        if iszero(facet) {
            mstore(0x00, ERR_FACET_NOT_FOUND)
            mstore(0x04, idx)
            revert(0x00, 0x24)
        }
        
        let size := sub(cds, stripLen)
        calldatacopy(0, 0, size)
        let ok := delegatecall(gas(), facet, 0, size, 0, 0)
        returndatacopy(0, 0, returndatasize())
            
        switch ok
        case 0 { revert(0, returndatasize()) }
        default { return(0, returndatasize()) }
    }
}
```

### Facet Identification

Each installed facet SHALL be associated with exactly one routing index within
the routing table.

A facet routing assignment associates a routing index with a facet contract
address and is represented by the following structure:

```solidity
pragma solidity ^0.8.29;

struct Facet {
    /// @dev Routing index.
    uint8 index;
    /// @dev Facet contract address.
    address facet;
}
```

Routing indices identify protocol modules rather than individual functions.

Multiple externally callable Protocol Functions MAY be implemented by the same
facet without requiring additional routing metadata.

Protocol-specific function selectors are not required to be globally unique
across different facets.

### Facet Management

Implementations MUST perform upgrades at the granularity of facets rather
than individual functions.

A compliant implementation MUST support the following atomic facet management
operations:

- installation of a new facet
- replacement of an existing facet
- removal of an existing facet

Implementations MAY expose these operations through
any authorization mechanism.

#### Ownership

Routers MUST provide an ownership mechanism responsible for authorizing
modifications to the routing table.

Ownership control MUST gate access to Atomic Updates. The ownership mechanism
is implementation-specific and is not prescribed by this standard. See
[Security Considerations](#security-considerations) for authorization
requirements.

#### Atomic Updates

Atomic Update is responsible for maintaining routing metadata consistency by
updating both the identifier collection and its auxiliary routing data
atomically.

Implementations MUST support any combination of the following operations
within a single Atomic Update:

- install multiple facets
- replace multiple facets
- remove multiple facets
- register supported interface identifiers
- unregister existing interface identifiers
- execute storage migrations

Either every modification succeeds, or the entire transaction reverts.

#### Storage Migration

Storage migrations MAY be supplied as part of an Atomic Update.

If supplied, a storage migration contract SHALL be executed through
`DELEGATECALL` immediately after the routing metadata has been updated.

This enables:

- Storage transformations between protocol versions
- Initialization of new storage slots
- Cleanup of deprecated storage

If the storage migration fails, the entire Atomic Update MUST revert.

If no migration is required, the upgrade omits the migration contract.

### Facet Management Interface

Facet management is the mechanism by which protocol behavior evolves.
The identifier collection determines the protocol composition, while its
auxiliary routing data describes and supports that composition. Atomic
Updates modify facet assignments and maintain the consistency of the
associated routing data.

The `IFacetManager` interface standardizes the Atomic Update workflow.
All post-initialization facet modifications (installation, replacement,
removal) MUST occur through this interface with atomic semantics: either
all modifications in a single transaction succeed, or the entire transaction
reverts.

Implementations MUST emit an `AtomicUpdate` event for every Atomic Update.
This event provides an immutable, queryable record of the operations
performed by every Atomic Update, enabling indexers, governance systems,
and auditing tools to track protocol evolution.

```solidity
pragma solidity ^0.8.29;

interface IFacetManager {
    /// @notice Atomically updates the protocol configuration.
    /// @param setF Facets to install, replace, or remove.
    /// @param addI ERC interface identifiers to register.
    /// @param remI ERC interface identifiers to unregister.
    /// @param migrator Optional storage migration contract.
    /// @param _calldata Encoded migration calldata.
    /// @dev Executes all routing and interface updates before performing
    ///      an optional storage migration.
    function atomicUpdate(
        Facet[] calldata setF, 
        bytes4[] calldata addI, 
        bytes4[] calldata remI,
        address migrator, 
        bytes calldata _calldata
    ) external;

    /// @notice Emitted after an atomic protocol update.
    /// @param setF Updated facet assignments.
    /// @param addI Registered interface identifiers.
    /// @param remI Unregistered interface identifiers.
    /// @param migrator Storage migration contract.
    /// @param _calldata Migration calldata.
    event AtomicUpdate(
        Facet[] setF, 
        bytes4[] addI, 
        bytes4[] remI, 
        address migrator, 
        bytes _calldata
    );
}
```

### Protocol Introspection

The `IObservability` interface enables external systems to inspect the
protocol's routing table and derived occupancy state. This is essential for:

- Wallets determining which functions a protocol supports
- Block explorers displaying protocol composition
- SDKs generating type-safe client interfaces
- On-chain governance systems querying routing state
- Auditing tools verifying protocol configuration

The observability functions provide complementary views of the routing table
and its occupancy state:

- **getFacets()** returns only the installed facet addresses (useful for
understanding protocol composition at the address level)
- **getFacetEntries()** returns the complete (index, address) mapping (provides
the full routing table for detailed inspection)
- **getFacetAt(index)** queries a specific slot (efficient for on-chain routing
table verification)
- **getFacetCount()** reports the number of installed facets (useful for
iteration and space utilization)
- **getFirstFreeSlot()** identifies available routing capacity (enables
efficient facet allocation during upgrades)

These functions operate in `view` mode and impose no state changes, making them
safe for external tooling and governance systems to call repeatedly.

```solidity
pragma solidity ^0.8.29;

interface IObservability {
    /// @notice Returns all installed facet addresses.
    /// @return Array of installed facet addresses.
    function getFacets() external view returns (address[] memory);

    /// @notice Returns all installed facet entries.
    /// @dev Each entry contains both the routing index and facet address.
    /// @return Array of installed facet entries.
    function getFacetEntries() external view returns (Facet[] memory);

    /// @notice Returns the facet installed at a routing index.
    /// @param index Routing index.
    /// @return Facet address, or the zero address if the slot is empty.
    function getFacetAt(uint8 index) external view returns (address);

    /// @notice Returns the number of installed facets.
    /// @return Number of occupied routing slots.
    function getFacetCount() external view returns (uint16);

    /// @notice Returns the first available routing slot.
    /// @return Index of the first unoccupied routing slot.
    /// @dev Reverts if all routing slots are occupied.
    function getFirstFreeSlot() external view returns (uint8);
}
```

### ERC-165 Integration

ERC-165 interface detection allows external systems to discover the
architectural and protocol interfaces supported by a router. Routers MUST
accurately report the interface identifiers they support, including:

- `0x01ffc9a7` (`IERC165`)
- `0x5378f98e` (`IFacetManager`)
- `0x1c60a259` (`IObservability`)

Interface identifiers exposed by installed facets MUST likewise be reported
through ERC-165. This allows contracts and external tooling to discover
architectural capabilities and protocol interfaces dynamically as facets are
installed, replaced, or removed.

### Initial State

Routers MUST initialize with three core facets installed at standard routing
indices:

- Index 0: `IFacetManager` (facet management)
- Index 1: Ownership (ownership mechanism)
- Index 2: `IERC165`, `IObservability` (interface detection and facet
  introspection)

The Ownership facet MUST provide an ownership mechanism authorizing routing
table modifications.

Initial deployment MAY emit an `AtomicUpdate` event documenting these core
facets, but this is OPTIONAL.

## Rationale

The architectural decisions of this specification follow directly from the
separation between routing and behavioral concerns introduced in the
[Motivation](#motivation). The remaining sections explain why the selected
realization of the Cento routing model was chosen over alternative
realizations.

### Why Explicit Facet Identity?

**Scaling**: selector-centric systems scale routing metadata with function
count. Facet-centric systems scale with facet count. Most protocols have far
fewer facets than externally callable functions.

**Atomicity**: Replacing a facet requires updating exactly one routing entry.
Selector-centric systems require updating multiple entries (one per function).

**Simplicity**: Protocol developers organize code around facets.
Facet-centric routing reflects this naturally.

**Collision Safety**: Because Protocol Routing identifies facets using
routing indices rather than function selectors, Protocol Functions do not
require globally unique selectors across facets, eliminating selector
collision concerns.

### Routing Index Width

This specification standardizes an 8-bit routing index (0–255). The choice of
an 8-bit routing index provides several advantages:

- **Minimal calldata overhead**: Protocol Routing requires only one additional
calldata byte.
- **Sufficient routing capacity**: Supports up to 256 facets, sufficient
for virtually all existing modular protocol architectures
- **Implementation simplicity**: Byte-aligned routing indices simplify
calldata processing and low-level implementations
- **Gas efficiency**: Introduces minimal routing overhead while eliminating
selector management within Protocol Routing

Standardizing the routing index width promotes interoperability between
compliant implementations.

Protocols requiring larger routing tables MAY extend the routing index width
or introduce additional routing mechanisms. Such extensions are outside the
scope of this specification, provided they preserve the routing semantics
defined herein.

### Storage Independence

Routing semantics are intentionally independent from the storage layout model.
Storage layout represents an implementation concern rather than an
interoperability concern.

Consequently, this standard neither requires nor discourages any particular
storage layout model. Future storage layout standards remain fully
compatible with this specification.

### Separation of Routing and Compatibility

In selector-centric routing models, function selectors serve two distinct
purposes: routing (identifying which facet should execute) and compatibility
(identifying which standardized interface is being invoked).
These concerns evolve under different constraints. Routing benefits from
facet-level abstraction. Compatibility requires backward-compatible selector
tables.

This specification separates these concerns by assigning routing and
compatibility to different architectural mechanisms: Protocol Routing uses
routing indices (implementation-focused), whereas Interface Compatibility
preserves selector-based interoperability for standardized Ethereum interfaces.
Each mechanism can evolve independently.

### Relationship to ERC-2535

ERC-2535 pioneered standardized modular proxy architectures.
This standard shares the same goal: enabling protocol modularity.

The architectural difference is fundamental:

- **ERC-2535**: selector-centric routing with implicit facet identity
- **This standard**: facet-centric routing with explicit facet identity

Both standards may coexist. Protocols prioritizing function-level routing
flexibility may prefer ERC-2535. Protocols prioritizing facet-level routing
efficiency and modularity should evaluate this standard.

### Facets as First-Class Protocol Components

Protocol developers organize software around implementation modules
rather than individual selectors.

Development, auditing, testing, deployment, upgrades, documentation, and
maintenance naturally occur at the facet level.

This specification therefore treats facets as first-class architectural
components, and thus routing metadata reflects software architecture
directly instead of indirectly through exported selectors.

### Scope

This specification standardizes routing semantics while intentionally leaving
implementation-specific concerns unspecified. In particular, it does not
mandate:

- storage layout
- authorization mechanism
- deployment model
- upgrade governance
- facet discovery algorithms
- routing metadata implementation

Implementations remain free to innovate within these areas while preserving
the interoperability defined by this specification.

This intentionally narrow scope also leaves room for future ERC standards to
define interoperable extensions, including:

- **Wider routing indices** for protocols with more than 256 facets
- **Hierarchical routing** for complex multi-protocol compositions
- **Standardized governance** for upgrade authorization mechanisms
- **Standardized storage migration** for common migration patterns
- **Facet descriptors** for standardized facet names, versions, and ABI
  descriptions

Such extensions remain compatible with this specification provided they
preserve the routing semantics defined by this specification.

## Backwards Compatibility

This specification preserves compatibility with existing Ethereum standards
through the Interface Compatibility routing adapter.

Compliant implementations remain compatible with existing tooling supporting
standardized Ethereum interfaces, including wallets, block explorers,
development frameworks, ABI encoding and decoding, and Solidity external
function calls.

This specification requires no modifications to Solidity, the EVM, ABI
encoding, or Ethereum protocol rules.

Because the router removes the routing index before delegated execution,
facets receive calldata identical to that of conventional contract calls.
Existing Solidity code may therefore be reused within facets without
modification.

Architectures realizing selector-centric routing models are not
automatically compliant with this specification because they employ a
different routing model.

Conversely, compliant implementations of this specification are not required
to expose selector-based routing metadata.

Selector-centric and facet-centric routing models are complementary
architectural approaches and may coexist within the Ethereum ecosystem.

## Reference Implementation

The reference implementation consists of the Solidity contracts, interfaces,
libraries, and structs implementing the protocol defined by this specification.
It focuses on the protocol logic, intentionally excludes deployment scripts,
tests, and development tooling, and comprises the following source files:

- [`CentoRouter.sol`](../assets/eip-8349/reference/CentoRouter.sol):
  Router implementation and protocol entry point.

- **`facets/`**
  - [`FacetManager.sol`](../assets/eip-8349/reference/facets/FacetManager.sol):
    Facet Management interface implementation
  - [`Observability.sol`](../assets/eip-8349/reference/facets/Observability.sol):
    Protocol introspection and ERC-165 interface implementation
  - [`Ownership.sol`](../assets/eip-8349/reference/facets/Ownership.sol):
    Ownership implementation conforming to the ERC-173 interface

- **`interfaces/`**
  - [`IERC173.sol`](../assets/eip-8349/reference/interfaces/IERC173.sol):
    ERC-173 ownership interface definition
  - [`IFacetManager.sol`](../assets/eip-8349/reference/interfaces/IFacetManager.sol):
    Facet Management interface definition
  - [`IObservability.sol`](../assets/eip-8349/reference/interfaces/IObservability.sol):
    Protocol introspection interface definition

- **`libraries/`**
  - [`LibBitmap.sol`](../assets/eip-8349/reference/libraries/LibBitmap.sol):
    Bitmap utilities for tracking routing index occupancy
  - [`LibCento.sol`](../assets/eip-8349/reference/libraries/LibCento.sol):
    Shared protocol storage access and facet management operations

- **`structs/`**
  - [`CentoStorage.sol`](../assets/eip-8349/reference/structs/CentoStorage.sol):
    Shared storage layout used by the router and facets
  - [`Facet.sol`](../assets/eip-8349/reference/structs/Facet.sol):
    Facet descriptor structure

The implementation uses an [ERC-7201](./eip-7201.md) storage namespace for
shared protocol state and tracks routing index occupancy using a compact
bitmap-based data structure.

## Security Considerations

### Authorization

Atomic Updates directly modify routing metadata and therefore directly modify
protocol composition. Consequently, implementations should:

- Restrict `atomicUpdate()` to authorized entities
- Consider time-locks, multi-signature authorization, or other governance
mechanisms for sensitive upgrades
- Log all routing metadata modifications

### Delegatecall Risks

Implementations relying on `DELEGATECALL` inherit the associated execution
risks:

- Delegated code executes in the router's storage context
- Storage layout collisions between facets can corrupt protocol state
- Malicious facets can steal funds or corrupt state

Mitigation:

- Use a storage layout model that prevents storage collisions (e.g., ERC-7201
namespacing)
- Audit all facets before installation
- Protect Atomic Updates through appropriate authorization and governance
mechanisms

### Facet Validation

Facets should be deployed smart contracts containing executable code.
Routers should reject installation of the following:

1. **EOAs (Externally Owned Accounts)**: Facets must not be externally owned
addresses. Delegation to an EOA will silently succeed without executing any
code, creating undefined protocol behavior.

2. **Empty contracts**: Facets must not be contracts with no code. This
includes newly created contracts or contracts that have self-destructed.

3. **[EIP-7702](./eip-7702.md) delegated EOAs**: EIP-7702 introduces a
mechanism allowing EOAs to delegate code execution. Routers should reject
EIP-7702 delegated EOAs as facets because they introduce a secondary
authorization path outside the router's control. An EOA may change its
delegation at any time, fundamentally altering protocol behavior without
modifying the routing table. This is a critical security boundary violation.

Implementations should perform bytecode validation before installing a facet.
The reference implementation detects EIP-7702 delegations by checking for the
EIP-7702 magic prefix (`0xef0100`) in the first three bytes of the account's
code.

Failure to validate facets during installation can result in:

- Silent routing failures (delegation to an EOA neither reverts nor executes
code)
- Unauthorized protocol modifications (EIP-7702 EOA changes delegation)
- Undefined behavior and security breaches

### Routing Validation

Routers must validate routing inputs and delegation targets before delegated
execution. Invalid routing inputs or delegation targets should cause the
transaction to revert before delegated execution.

Implementations should minimize the amount of work performed before completing
this validation.

Routers should validate the following:

1. **Non-zero calldata**: Empty calldata must not be processed by fallback
routing. Use `receive()` or perform an explicit calldata-length check.
2. **Valid indices**: Routing indices must resolve installed facets.
3. **No zero addresses**: Routers must not delegate to address(0).
4. **No self-routing**: Routers must not delegate to themselves.

Failure to validate can result in:

- Gas exhaustion (underflow causing oversized `CALLDATACOPY`)
- Silent failures (zero address delegation)
- Unexpected self-delegation (execution delegated back to the router)

### Empty Calldata

Protocol Routing removes the routing index from calldata before delegation.

Attempting to remove a routing index from empty calldata may underflow the
computed calldata length, causing `CALLDATACOPY` to attempt copying an
effectively unbounded memory region and exhausting available gas.

Implementations should prevent fallback routing from processing
empty calldata.

Implementations should provide a `receive()` function that consumes
empty calldata before fallback routing logic is reached. Alternatively,
add an explicit check in `fallback()` itself before copying calldata.

Equivalent protection mechanisms are also acceptable.

### Selector Collisions

While Protocol Functions do not require unique function selectors to resolve
target facets, Compatibility Functions do.

Consequently, custom Compatibility Functions should ensure that their
selectors do not collide with standardized Ethereum interfaces or other
Compatibility Functions exposed by the router.

### Storage Migrations

If used, storage migrations introduce upgrade risks:

- Migrations must preserve protocol invariants
- Migrations should be independently audited
- If migration fails, the entire Atomic Update must revert
- Multi-step migrations should employ careful sequencing to prevent
partial state corruption

### Facet Upgrades

Installing, replacing, or removing facets changes protocol behavior.
Implementations should:

- Ensure new facets have been audited before installation
- Use governance time-locks before activation
- Perform incremental testing before full deployment
- Maintain rollback procedures

### Immutable Router

Router immutability reduces the trusted computing base by ensuring that
routing behavior cannot itself be modified after deployment.
Implementations should prefer immutable routers whenever practical, because
they:

- Reduce attack surface (no router logic upgrades)
- Clarify responsibility (facets contain logic)
- Improve auditing (fixed routing algorithm)

If router upgrades are supported, they should be protected by authorization
mechanisms at least as strong as those governing Atomic Updates.

## Copyright

Copyright and related rights waived via [CC0](../LICENSE.md).
