Proxy Functions in Ethereum
- Proxy Functions (PFs) are mechanisms in Ethereum that intercept, validate, and delegate calls via fallback(), receive(), and DELEGATECALL for upgradeable contract designs.
- They integrate key functions like upgradeTo and changeAdmin to manage governance and ensure state consistency between proxy and logic contracts.
- Empirical studies show proxies dominate deployment scenarios, highlighting both operational benefits and potential risks such as function and storage collisions.
Proxy functions (PFs) in Ethereum are the behaviors and entrypoints through which a proxy contract intercepts an incoming call, optionally enforces preconditions or access control, and forwards execution to a target logic contract under the proxy’s storage context. In Solidity and EVM terms, PFs are centered on fallback() and receive() together with DELEGATECALL; in upgradeable designs they also include upgrade and governance entrypoints such as upgradeTo, upgradeToAndCall, admin, and changeAdmin. Across recent empirical studies, PFs are treated as the operative surface of the proxy pattern: one formulation emphasizes proxy-side interception and upgrade control, while another situates PFs at the broader dispatch boundary between proxy-defined functions and logic-defined functions reached through delegated execution (Ebrahimi et al., 1 Jan 2025, Chen et al., 2024).
1. Definition and architectural role
A proxy contract is defined as a deployed smart contract that forwards calls to another contract and executes that other contract’s code in the proxy’s storage context. Under this architecture, users transact with the proxy rather than the logic contract directly. The proxy holds state and a reference to the current implementation, while the logic contract encodes behavior. The proxy pattern therefore separates address stability and state continuity from implementation mutability, which is the basis for upgradeability as well as for cheap cloning and modular composition (Ebrahimi et al., 1 Jan 2025, Chen et al., 2024).
The narrowest definition of PFs consists of the proxy-local mechanisms that realize this architecture. These include interception through fallback() or receive(), optional checks such as access control, delegation to the implementation via DELEGATECALL, and, for upgradeable systems, administrative functions that update the implementation reference. A broader formulation additionally treats the proxy-defined functions and the logic-defined functions reachable through proxy dispatch as a single callable surface, because selector matching and shared storage jointly determine the contract’s observable behavior (Chen et al., 2024).
This distinction matters because PFs are not merely syntactic artifacts of a proxy contract. They are the dispatch interface that governs whether a call is handled locally, delegated transparently, or rejected; whether implementation changes are possible; and whether governance actions remain isolated from user-facing logic. In practical terms, PFs define the control plane of upgradeable and forwarding-based systems.
2. Execution semantics of interception, delegation, and upgrade
The core PF pipeline is stable across proxy families. A call arrives at the proxy; fallback() and/or receive() capture calls not matched by a declared interface; the proxy may apply precondition checks or access control; the proxy then issues DELEGATECALL to the logic contract using the same 4-byte function selector; returndata is bubbled back to the caller; and failure in the delegated execution causes a revert (Ebrahimi et al., 1 Jan 2025).
A representative forwarding motif is:
1 2 3 4 5 6 7 |
def _fallback() payable: delegate stor0 with: funct call.data[0:4] gas gas_remaining args call.data[4:] if not delegate.return_code: revert with return_data return return_data |
This pattern preserves the proxy’s storage layout while executing logic code. As a result, state variables such as ownership, balances, and configuration remain attached to the proxy address even though the implementation can be changed over time. The preservation of the original 4-byte selector is operationally important because transparent forwarding depends on the delegated contract receiving the same function identifier that the caller supplied (Ebrahimi et al., 1 Jan 2025).
Upgradeable PFs extend this dispatch pipeline with implementation-management routines. Typical functions are upgradeTo(address newImplementation) and upgradeToAndCall(address newImplementation, bytes data), usually guarded by admin-only checks. These are often accompanied by governance functions such as admin() and changeAdmin(address). In transparent proxies, the implementation and admin addresses are kept in fixed storage slots, the fallback delegates to the implementation for non-admin users, and the admin is prevented from accidentally traversing the proxied user interface, which mitigates selector clashes (Ebrahimi et al., 1 Jan 2025, Chen et al., 2024).
The underlying selector mechanism is the first 4 bytes of the Keccak-256 hash over the canonical function prototype string:
$s = \mathrm{first4bytes}(\mathrm{keccak256}(\text{"f(t_1,t_2,...)"}))$
Because PF dispatch relies on selector matching, proxy-local functions and delegated logic functions compete at the 4-byte level. That property is central both to ordinary routing behavior and to collision vulnerabilities (Chen et al., 2024).
3. Functional taxonomy and canonical proxy designs
The proxy literature represented here divides PF-bearing contracts into two functional categories. Interceptor proxies, also called forwarders, primarily implement the fallback-and-delegate behavior and may hard-code the target implementation or derive it from a factory or registry. Upgradeability proxies expose PFs that can alter the implementation reference over time, either directly on the proxy, through a beacon, or through logic-side upgrade routines in a UUPS arrangement (Ebrahimi et al., 1 Jan 2025).
| PF category or design | Representative pattern | PF characteristics |
|---|---|---|
| Interceptor proxy | Forwarder, ERC-1167 | Fallback-mediated delegation |
| Upgradeability proxy | Transparent/EIP-1967, Beacon, UUPS/EIP-1822 | Upgrade and governance entrypoints |
| Multi-facet proxy | Diamond/EIP-2535 | Selector routing across facets |
In a statistically representative manual classification, 67.8% of proxies are interceptors or forwarders and 32.2% enable upgradeability. In the same study, 79% of proxies adhere to known reference implementations and 21% are customized, with ERC-1167 minimal proxies alone accounting for 29.4% (Ebrahimi et al., 1 Jan 2025). A separate large-scale census of alive contracts reports 89.05% minimal proxies, 1.00% EIP-1967, 0.12% EIP-1822, and 9.83% non-standard proxies, indicating that PFs are most often instantiated through compact forwarding shells rather than heavily customized code paths (Chen et al., 2024).
The standard designs differ in where PF logic resides. ERC-1167 minimal proxies are tiny clones that embed the implementation address in bytecode; their sole PF is a fallback that delegates to the encoded implementation, and detection depends on bytecode signatures rather than storage slots. Transparent proxies and related EIP-1967-style contracts store implementation and admin addresses in fixed slots and expose admin-gated upgrade PFs. UUPS moves the upgrade authority into the logic contract while preserving the same storage slot in the proxy’s storage context. Beacon proxies read the implementation via a beacon getter such as implementation() and place the upgrade PFs in the beacon itself. Diamond proxies route selectors to facets and support atomic function replacement, but remain grounded in the same fallback-and-delegate principles (Ebrahimi et al., 1 Jan 2025).
For EIP-1967, the standardized slot for the implementation pointer is:
and corresponding admin and beacon slots follow the same pattern. UUPS or ERC-1822 derives its implementation slot from (Chen et al., 2024).
4. Prevalence, deployment pathways, and ecosystem usage
PFs are not peripheral to Ethereum. One large-scale study covering all Ethereum activity from Aug-07-2015 to Sep-01-2022 analyzes 50,845,833 deployed smart contracts, 1,695,517,186 transactions, and 5,503,071,306 traces, and reports that 14.2% of all deployed contracts are active proxies. Using CCDFs of inbound transactions and a one-tailed Mann–Whitney U test with and Cliff’s delta interpretation thresholds, it concludes that proxy contracts are more actively used than non-proxy contracts. Across usage contexts, stakeholder adoption, and transaction involvement, proxy usage rises steadily and peaks at the end of the study period (Ebrahimi et al., 1 Jan 2025).
A second study analyzes over 36 million alive contracts from 2015 to Oct 2023 and reports that 54.2% of them are proxy contracts. It further states that in 2022–2023 more than 93% of deployments used the proxy pattern. The difference between the two prevalence figures reflects different datasets, denominators, and detection scopes: one measures active proxies among all deployed contracts up to September 2022, while the other measures proxies among alive contracts through October 2023 and explicitly targets hidden contracts that lack both source code and past transactions (Chen et al., 2024).
Deployment pathways show how PFs are operationalized at scale. Proxies are deployed either directly via externally owned accounts using off-chain scripts or through on-chain factory contracts, with the former and latter accounting for 39.1% and 60.9% of identified usage contexts, respectively. Off-chain contexts represent 39.07% of usage contexts but only 0.69% of proxy instances, or 50,174 proxies, indicating small-scale and bespoke deployment. On-chain patterns represent 99.3% of proxy instances and are dominated by EOA > FA > P with 6,618,012 proxies and 39.81% of contexts; deeper chains such as FA > FA > P, FA > FA > FA > FA > P, PF > P, and PF > PF > P support standardized PF configuration and mass cloning (Ebrahimi et al., 1 Jan 2025).
The ecosystem examples are correspondingly large. Uniswap V1 used 1,740 minimal proxies delegating to a single logic contract and achieved 14× lower total deployment cost than redeploying full logic per user. Gnosis Safe exhibits factory-based chains with 51,746 wallet proxies. OpenSea created 943,022 OwnableDelegateProxy instances through a registry factory. Kraken and Dharma illustrate multi-layer proxy and beacon arrangements in which PF governance and validation are distributed across several contracts (Ebrahimi et al., 1 Jan 2025).
5. Detection, classification, and measurement of PFs
Behavioral analysis treats PFs as runtime phenomena. A proxy detector can mine traces, identify a candidate proxy as any contract initiating a DELEGATECALL, and then confirm proxy behavior by verifying that the parent trace’s function selector matches the selector observed at the delegated call. This establishes that the contract forwards the caller’s requested function transparently to its implementation. On a manually curated ground truth of 385 contracts, evaluated with precision, recall, and ,
the method attains perfect precision, overall recall of 68.9% because dormant or never-triggered proxies remain unobserved, and both precision and recall of 100% for active proxies. Storage-slot checks are not required for detecting proxy behavior itself; they are used only when classifying reference implementations (Ebrahimi et al., 1 Jan 2025).
Static and emulation-based analysis broadens coverage to hidden proxies. Proxion disassembles bytecode, filters out contracts without DELEGATECALL, and emulates candidate contracts with carefully crafted calldata using a random 4-byte selector chosen to avoid any observed proxy function selectors recovered from PUSH4 occurrences. If fallback execution forwards the original calldata via DELEGATECALL, the contract is marked as a proxy; if not, it is treated as a non-proxy, such as a library-like caller. The system extends opcode emulation to handle chain-dependent values, simulates CALL and DELEGATECALL with paired emulator instances, and uses a fixed-address approach for CREATE and CREATE2 (Chen et al., 2024).
Logic-address recovery depends on the proxy design. Minimal proxies expose the implementation address in bytecode. Storage-based proxies require inferring the relevant slot from the DELEGATECALL stack inputs and reconstructing historical slot values. To scale this process, Proxion assumes logic addresses for a given proxy are unique over time and uses binary search over block heights, averaging 26 getStorageAt calls per proxy (Chen et al., 2024).
Collision analysis also operates at the PF boundary. With source code, selectors can be computed directly and intersected. Without source code, Proxion uses Panoramix-disassembled bytecode, jump analysis, and the PUSH4 → EQ → JUMPI dispatch pattern to extract selectors only from actual dispatch chains. On the Smart Contract Sanctuary dataset, it reports 99.5% accuracy for function collisions and 78.2% for storage collisions. At full-dataset scale it reports 1,566,784 function-collision pairs and 3,022 storage-collision pairs across 19.6 million proxy–logic pairs, while processing proxy checks in approximately 6.4 ms per contract and finishing 36 million contracts in roughly 65 hours on a 12-core commodity server (Chen et al., 2024).
6. Collision surfaces, mitigation, and unresolved questions
PFs create two principal collision surfaces. Function collisions arise when a proxy-defined function and a logic-defined function share the same 4-byte selector. Because EVM dispatch first matches selectors against explicitly defined proxy functions, a colliding proxy-local function shadows the logic function and prevents fallback delegation for that selector. The reported honeypot-like example pairs a proxy function impl_LUsXCWD2AKCc() with a logic function free_ether_withdrawal(), both hashing to 0xdf4a3106; calls intended for the logic function are intercepted locally. The same study reports finding a colliding prototype for free_ether_withdrawal() after roughly 600 million attempts in 1.5 hours on a commodity laptop, demonstrating that targeted 4-byte collisions are practically attainable (Chen et al., 2024).
Storage collisions arise because logic executes in the proxy’s storage context. If proxy and logic variables map to the same slot, including packed Solidity layouts within a 32-byte word, reads and writes can overwrite or reinterpret state. The simplified Audius case places the proxy’s owner at slot 0 and the logic’s initialized and initializing booleans, packed together, also at slot 0. The resulting overlap enabled re-initialization and governance takeover. The paper also cites a major bridge-contract collision disclosed through a bug bounty, underscoring that storage-layout errors in PF-based upgradeability are not merely theoretical (Chen et al., 2024).
Mitigation practices follow directly from these failure modes. Standardized slots such as EIP-1967’s implementation, admin, and beacon slots reduce accidental overlap with ordinary Solidity layouts. Transparent proxies prevent accidental function collisions by separating admin and non-admin dispatch. Upgrade safety requires maintaining consistent storage layout across logic versions, appending rather than reordering variables, and validating initializer behavior. Minimal proxies are described as collision-free by design because they declare virtually no functions or variables beyond the minimal dispatcher. For operational security, upgrade PFs should be gated by multi-signature or on-chain governance rather than single-EOA admins, and implementation addresses should be published so that proxy–logic relationships are auditable (Ebrahimi et al., 1 Jan 2025, Chen et al., 2024).
Several research questions remain open. One study calls for automated, large-scale classification of interceptor versus upgradeability proxies and better support for the 21% of customized designs not captured by reference signatures; it also highlights system-level analyses of DApp architectures, release-engineering metrics for upgradeable PFs, governance mapping, dormant-proxy detection, and measurement of gas and transparency trade-offs (Ebrahimi et al., 1 Jan 2025). The other identifies diamond proxies as a current blind spot for bytecode-level analysis when selector tables are only recoverable from registered facets, and notes residual emulation errors as well as the need to reason about massive duplication, where millions of near-identical proxies may propagate the same PF misconfiguration or inherited collision pattern across an ecosystem (Chen et al., 2024).