ERC-7746 Composable security middleware hooks

Last Call · Other · created 2024-07-17

Defines a standard interface, ILayer, that lets a smart contract call out to external security layers before and after function execution to delegate validation. It separates security checks from the core logic so they can be composed and swapped.

Why it is needed

Security validation logic in smart contracts is usually bundled inside the contract body itself. When code such as access control or input checks is fused together with business logic, the code becomes tightly coupled, making it hard to examine or fix security issues in isolation. Changing a single check requires touching the entire contract, and well-built validation logic is difficult to reuse as-is in other contracts. The software industry widely uses a middleware pattern, wrapping a call with common pre- and post-processing checks under the same call signature, but smart contracts lacked a common interface for this kind of insertion. ERC-7746 defines a standard hook interface called before and after function execution, separating security checks into an independent layer that can be composed or upgraded as needed.

In depth

Motivation for the standard: from monolithic validation to middleware

Smart contract security today often relies on bundling validation logic inside the contract, which tightly couples code and makes it hard to separate security concerns. The source introduces the widely used middleware pattern from industry, a general, repeatable pattern of wrapping a call with another call under the same call signature. This lets specialized security providers develop and maintain specific checks independently (independent security providers), combine layers to build a per-contract security profile (composability), update layers without changing the protected contract (upgradeability), and perform a wide range of checks such as access control, input sanitization, and output validation (flexibility). It also lays the groundwork for security providers to build general-purpose security-oracle-as-a-service offerings.

The ILayer interface: beforeCall and afterCall

A contract implementing ILayer must provide two functions. beforeCall is called before execution, receiving per-layer configuration data (configuration), the function selector (selector), the address that initiated the call (sender), the ETH amount sent (value), and the call data (data), and must revert if validation fails. The arbitrary data beforeCall returns (beforeCallResult) is passed unchanged to afterCall after execution, enabling validation that records pre-execution state and compares it against post-execution state. afterCall receives the same call information plus beforeCallResult, and must likewise revert on validation failure.

Integration rules for the protected contract

A protected contract MAY integrate layers by calling a pre-validation function before executing its own logic and a post-validation function afterward. It can register multiple layers to run in a fixed order, and if any single layer reverts, the protected contract MUST also revert. This is a safety-first design where rejection by a single layer cancels the entire transaction. Note that while the spec text refers to these calls as beforeCallValidation and afterCallValidation, the function names declared in the interface code are beforeCall and afterCall.

Design rationale: configuration data, state retention, strict validation, gas cost

The per-layer configuration parameter (referred to as layerConfig in the source's Rationale) carries layer-specific settings, letting a single layer implementation serve multiple contracts with different requirements. Because layer calls are not static calls, a layer can maintain its own state and implement complex validation logic such as rate limiting or usage tracking. Forcing a revert on validation failure acts as a safeguard against executing potentially dangerous transactions. Layer execution carries a gas cost, but the source argues that the benefits of security and modularity outweigh the cost, and expects gas costs to fall over time.

Reference implementation: RateLimitLayer tested against a fund-draining attack

The reference implementation repository contains the ILayer.sol interface along with Protected.sol, RateLimitLayer.sol, and a Drainer contract. The Protected contract is assumed to have a bug that allows partial unauthorized access to its state, and the Drainer mimics this vulnerable scenario by maliciously attempting to drain funds. RateLimitLayer implements ILayer to enforce a client-configured rate limit, and in this test it is configured so that the same sender can send at most 10 transactions per block. The test in test.ts confirms that under this condition the Drainer cannot drain Protected's funds.

Progress status: Last Call

This proposal is a Standards Track ERC and is currently in Last Call per the source. The final comment period deadline is stated as July 29, 2025. It should be kept in mind that this standard has not yet reached the Final stage.

Things to watch out for

Original ERC · ← ERCs